Quintus
Prolog Manual
Quintus Prolog uses three data areas: program space, local stack space, and global stack space. Each of these areas is automatically expanded if it overflows; if necessary, the other areas are shifted to allow this. The local stack contains all the control information and variable bindings needed in a Prolog execution. Space on the local stack is reclaimed on determinate success of predicates and by tail recursion optimization, as well as on backtracking. The global stack space contains the heap (also known as the global stack) and the trail. The heap contains all the data structures constructed in an execution of the program, and the trail contains references to all the variables that need to be reset when backtracking occurs. Both of these areas grow with forward execution and shrink on backtracking. These fluctuations can be monitored by statistics/[0,2]. The program space contains compiled and interpreted code, recorded terms, and atoms. The space occupied by compiled code, interpreted code, and recorded terms is recovered when it is no longer needed; the space occupied by atoms which are no longer in use can be recovered by atom garbage collection described in {manual(g-12-8)}. Quintus Prolog uses the heap to construct compound terms, including lists. Heap space is used as Prolog execution moves forward. When Prolog backtracks, it automatically reclaims space on the heap. However, if a program uses a large amount of space before failure and backtracking occur, this type of reclamation may be inadequate. Without garbage collection, the Prolog system must attempt to expand the heap whenever a heap overflow occurs. To do this, it first requests additional space from the operating system. If no more space is available, the Prolog system attempts to allocate unused space from the other Prolog data areas. If additional space cannot be found, a resource error is raised. Heap expansion and abnormal termination of execution due to lack of heap space can occur even if there are structures in the heap that are no longer accessible to the computation (these structures are what is meant by "garbage"). The proportion of garbage to non-garbage terms varies during execution and with the Prolog code being executed. The heap may contain no garbage at all, or may be nearly all garbage. The garbage collector periodically reclaims inaccessible heap space, reducing the need for heap expansion and lessening the likelihood of running out of heap. When the garbage collector is enabled (as it is by default), the system makes fewer requests to the operating system for additional space. The fact that less space is required from the operating system can produce a substantial savings in the time taken to run a program, because paging overhead can be much less. For example, without garbage collection, compiling a file containing the sequence
p(_) :- p([a]). :- p(_).
causes the heap to expand until the Prolog process eventually runs out of space. With garbage collection enabled, the above sequence continues indefinitely. The list built on the heap by each recursive call is inaccessible to future calls (since p/1 ignores its argument) and can be reclaimed by the garbage collector. Garbage collection does not guarantee freedom from out-of-space errors, however. Compiling a file containing the sequence
p(X) :- p([X]). :- p(a).
expands the heap until the Prolog process eventually runs out of space. This happens in spite of the garbage collector, because all the terms built on the heap are accessible to future computation and cannot be reclaimed.
trimcore/0 reclaims space in all of Prolog's data areas. At any given time, each data area contains some free space. For example, the local stack space contains the local stack and some free space for that stack to grow into. The data area is automatically expanded when it runs out of free space, and it remains expanded until trimcore/0 is called, even though the stack may have shrunk considerably in the meantime. The effect of trimcore/0 is to reduce the free space in all the data areas as much as possible, and to give the space no longer needed back to the operating system. trimcore/0 is called each time Prolog returns to the top level or the top of a break level.
Statistics relating to memory usage, run time, and garbage collection, including information about which areas of memory have overflowed and how much time has been spent expanding them, can be displayed by calling statistics/0. The output from statistics/0 looks like this:
memory (total) 377000 bytes: 350636 in use, 26364 free
program space 219572 bytes
atom space (2804 atoms) 61024 in use, 43104 free
global space 65532 bytes: 9088 in use, 56444 free
global stack 6984 bytes
trail 16 bytes
system 2088 bytes
local stack 65532 bytes: 356 in use, 65176 free
local stack 332 bytes
system 24 bytes
0.000 sec. for 0 global and 0 local space shifts
0.000 sec. for 0 garbage collections which collected 0 bytes
0.000 sec. for 0 atom garbage collections which collected 0 bytes
0.233 sec. runtime
Note the use of indentation to indicate sub-areas. That is, memory contains the program space, global space, and local stack, and the global space contains the global stack and trail. The memory (total) figure shown as "in use" is the sum of the spaces for the program, global and local areas. The "free" figures for the global and local areas are for free space within those areas. However, this free space is considered used as far as the memory (total) area is concerned, because it has been allocated to the global and local areas. The program space is not considered to have its own free space. It always allocates new space from the general memory (total) free area. Individual statistics can be obtained by statistics/2, which accepts a keyword and returns a list of statistics related to that keyword. The keys and values for statistics(Keyword, List) are summarized below. The keywords 'core' and 'heap' are included to retain compatibility with DEC-10 Prolog. Times are given in milliseconds and sizes are given in bytes.
_____________________________________________________________________________
Keyword List
runtime [cpu time used by Prolog, cpu time since
last call to statistics/[0,2](in millisecs)]
memory [total Prolog memory in use, total Prolog memory free]
program [program space, 0]
stacks [total global stack memory, total local stack memory]
global_stack [global stack in use, global stack free]
local_stack [local stack in use, local stack free]
trail [size of trail, 0]
garbage_collection
[number of GCs, freed bytes, time spent]
stack_shifts [number of global stack area shifts, number of
local stack area shifts, time spent shifting]
atoms [number of atoms, atom space in use,
atom space free]
atom_garbage_collection
[number of AGCs, freed bytes, time spent]
core (same as memory)
heap (same as program)
_____________________________________________________________________________
For the keywords 'program' and 'trail', the second element of the returned list is always 0. This is for backward compatibility only, 0 being the most appropriate value in the Quintus Prolog system for the quantities that would be returned here in DEC-10 Prolog and previous releases of Quintus Prolog . To see an example of the use of each of these keywords, type
| ?- statistics(K, L).
and then repeatedly type ';' to backtrack through all the possible keywords. As an additional example, to report information on the runtime of a predicate p/0, add the following to your program:
:- statistics(runtime, [T0| _]),
p,
statistics(runtime, [T1|_]),
T is T1 - T0,
format('p/0 took ~3d sec.~n', [T]).
The availability of garbage collection can lead to a more natural programming style. Without garbage collection, a procedure that generates heap garbage may have to be executed in a failure-driven loop. Failure-driven loops minimize heap usage from iteration to iteration of a loop via Quintus Prolog's automatic recovery of heap space on failure. For instance, in the following procedure echo/0 echoes Prolog terms until it reads an end-of-file character. It uses a failure-driven loop to recover inaccessible heap space.
echo :- repeat,
read(Term),
echo_term(Term),
!.
echo_term(Term) :-
Term == end_of_file.
echo_term(Term) :-
write(Term), nl,
fail.
Any heap garbage generated by read/1 or write/1 is automatically reclaimed by the failure of each iteration. Although failure-driven loops are an accepted Prolog idiom, they are not particularly easy to read or understand. So we might choose to write a clearer version of echo/0 using recursion instead, as in
echo :- read(Term),
echo_term(Term).
echo_term(Term) :-
Term == end_of_file,
!.
echo_term(Term) :-
write(Term), nl,
echo.
Without garbage collection the more natural recursive loop accumulates heap garbage that cannot be reclaimed automatically. While it is unlikely that this trivial example will run out of heap space, larger and more practical applications may be unable to use the clearer recursive style without garbage collection. With garbage collection, all inaccessible heap space will be reclaimed by the garbage collector. Using recursion rather than failure-driven loops can improve programming style further. We might want to write a predicate that reads terms and collects them in a list. This is naturally done in a recursive loop by accumulating results in a list that is passed from iteration to iteration. For instance,
collect(List) :-
read(Term),
collect_term(Term, List).
collect_term(Term, []) :-
Term == end_of_file,
!.
collect_term(Term, [Term|List0]) :-
collect(List0).
For more complex applications this sort of construction might prove unusable without garbage collection. Instead, we may be forced to use a failure-driven loop with side-effects to store partial results, as in the following much less readable version of collect/1:
collect(List) :-
repeat,
read(Term),
store_term(Term),
!,
collect_terms(List).
store_term(Term) :-
Term == end_of_file.
store_term(Term) :-
assertz(term(Term)),
fail.
collect_terms([M|List]) :-
retract(term(M)),
!,
collect_terms(List).
collect_terms([]).
The variable bindings made in one iteration of a failure-driven loop are unbound on failure of the iteration. Thus partial results cannot simply be stored in a data structure that is passed along to the next iteration. We must instead resort to storing partial results via side-effects (here, assertz/1) and collect (and clean up) partial results in a separate pass. The second example is much less clear to most people than the first. It is also much less efficient than the first. However, if there were no garbage collector, larger examples of the second type might be able to run where those of the first type would run out of memory.
The user has the option of executing programs with or without garbage collection. Procedures that do not use a large amount of heap space before backtracking may not be affected when garbage collection is enabled. Procedures that do use a large amount of heap space may execute more slowly due to the time spent garbage collecting, but will be more likely to run to completion. On the other hand, such programs may run faster when the garbage collector is enabled because the virtual memory is not expanded to the extent that "thrashing" occurs. gc/0 and nogc/0 are the built-in predicates that are used to enable and disable the garbage collector. Alternatively, the gc prolog flag can be set to on or off. To run the gc in a verbose mode, set the gc_trace flag to on. By default, garbage collection is enabled.
By default, the user is given no indication that the garbage collector is operating. If no program ever runs out of space and no program using a lot of heap space requires an inordinate amount of processing time, then such information is unlikely to be needed. However, if a program thought to be using much heap space runs out of space or runs inordinately slowly, the user may want to determine whether more or less frequent garbage collections are necessary. Information obtained from the garbage collector by turning on the gc_trace option of prolog_flag/3 can be helpful in this determination.
For most programs, the default settings for the garbage collection parameters should suffice. For programs which have high heap requirements, the default parameters may result in a higher ratio of garbage collection time to run time. These programs should be given more space in which to run. The gc_margin is a non-negative integer specifying the desired margin in kilobytes. For example, the default value of 1000 means that the heap will not be expanded if garbage collection can reclaim at least one megabyte. The advantage of this criterion is that it takes into account both the user's estimate of the heap usage and the effectiveness of garbage collecting.
The correct value for the gc_margin is dependent upon many factors. Here is a non-prioritized list of some of them:
The algorithm used when the heap overflows is as follows:
if 'gc' is 'on'
and the heap is larger than gc_margin kilobytes then
garbage collect the heap
if less than gc_margin bytes are reclaimed then
try to expand the heap
endif
else
try to expand the heap
endif
The user can use the gc_margin option of prolog_flag/3 to reset the gc_margin (see {manual(g-10)}). If a garbage collection reclaims at least the gc_margin kilobytes of heap space the heap is not expanded after garbage collection completes. Otherwise, the heap is expanded after garbage collection. This expansion provides space for the future heap usage that will presumably occur. In addition, no garbage collection occurs if the heap is smaller than gc_margin kilobytes.
NOTE: prolog_flag(gc_margin, Old, New) has nothing to do with the gcguide(margin, Old, New) of older Prolog systems. The "margin" of those other systems was used for entirely different purposes.
Normally, the garbage collector is invoked only when some Prolog data area overflows, so the time of its invocation is not predictable. In some applications it may be desirable to invoke the garbage collector at regular intervals (when there is known to be a significant amount of garbage on the heap) so that the time spent garbage collecting is more evenly distributed in the processing time. For instance, it may prove desirable to invoke the garbage collector after each iteration of a question-and-answer loop which is not failure-driven. In rare cases the default garbage collection parameters result in excessive garbage collecting costs or heap expansion, and the user cannot tune the gc_margin parameter adequately. Explicitly invoking the garbage collector using the built-in predicate garbage_collect/0 can be useful in these circumstances.
This section describes the various system parameters required to run Prolog. Under UNIX, there is normally no need for you to seek any special privileges or quotas in order to run Prolog. Prolog will automatically expand its space up to the total amount of virtual space you are allowed. If it should run out of space, Prolog will raise a resource error. This may happen because of an infinite recursion in your program, or it may be that your program really needs more space than is available. If you are using the C shell (csh), you can find out how much space is available by means of the csh command "limit". The command
<prompt>limit
will list a number of limits of which the relevant one is "datasize". This number is the number of kilobytes available to Prolog for its data areas. You can reduce this limit by typing, for example,
<prompt>limit datasize 2000
The main reason that you might want to reduce the limit is that there is a bug in some UNIX systems which causes them to allow the allocation of more virtual memory than there is swap space available, and then to crash. You can run quite large programs with a datasize of 2000 kilobytes.
UNIX Caveat: On some UNIX systems, the specified 'datasize' (program) limit (see limit(csh) and getrlimit(3)) can be grossly higher then the maximum break that a process can set. This is because the setting of the break is dependent upon the amount of swap space available. Since all processes share the same swap space, the space available to any one process is based on the space usage of all other processes running on the machine. Therefore, one process that has set a large program break may prevent another process from doing the same, if both are running simultaneously.
The Quintus Prolog memory manager makes calculations based upon the specified 'datasize' limit, since the actual limit cannot be determined except by experimentation, and even then the limit changes over time. Better memory management will result when the specified 'datasize' limit is close to the actual limit. The default behavior of Prolog is tuned to be optimal for a large class of programs. If the programmer needs greater control of the way Prolog grows and frees memory, they can set the environment variables: PROLOGINITSIZE, PROLOGMAXSIZE, PROLOGINCSIZE, PROLOGKEEPSIZE, PROLOGLOCALMIN, PROLOGGLOBALMIN. The documentation for these variables follow. Note that the default values for these variables should satisfy almost all programs and you really do not need to set these variables at all.
Environment variable PROLOGINITSIZE controls the size of Prolog's initial memory allocation. PROLOGINITSIZE can be set to a sufficiently large size to allow the Prolog application to execute without needing to expand. This must be done before Prolog is invoked. The values for PROLOGINITSIZE are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. For example, one of the following commands, depending upon which shell is being used, can be used to set PROLOGINITSIZE to 2 megabytes:
setenv PROLOGINITSIZE 2M
or
PROLOGINITSIZE=2M; export PROLOGINITSIZE
By default, the PROLOGINITSIZE is the minimum memory required for Prolog to start up. In addition, PROLOGINITSIZE is constrained to be at least that amount, regardless of the user setting.
The environment variable PROLOGMAXSIZE can be used to place a limit on the amount of data space which a given Prolog process will use. The values for PROLOGMAXSIZE are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. One of the following commands, depending upon which shell is being used, can be used to set PROLOGMAXSIZE to 3 megabytes:
setenv PROLOGMAXSIZE 3M
or
PROLOGMAXSIZE=3M; export PROLOGMAXSIZE
The csh command "limit" can also be used to set the amount of data space which can be used by the the current shell and all processes within it. By default, PROLOGMAXSIZE is effectively infinity, which is to say that Prolog's expansion will only be limited by the space that the shell is able to provide it.
The environment variable PROLOGINCSIZE can be used to control the amount of space Prolog asks the operating system for in any given memory expansion. The values for PROLOGINCSIZE are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. For example, one of the following commands, depending upon which shell is being used, can be used to set PROLOGINCSIZE to 250 kilobytes:
setenv PROLOGINCSIZE 250K
or
PROLOGINCSIZE=250K; export PROLOGINCSIZE
By default, the PROLOGINCSIZE is the minimum amount of memory that will allow Prolog to expand one of its data areas, by kilobytes. In addition, PROLOGINCSIZE is constrained to be at least that amount, regardless of the user setting.
The environment variable PROLOGKEEPSIZE can be used to control the amount of space Prolog retains after performing some computation. By default, Prolog gets memory from the operating system as the user program executes and returns all the free memory back to the operating system when the user program does not need any more. If the programmer knows that her program once it has grown to a certain size is likely to need as much memory for future computations, then she can advise Prolog not to return all the free memory back to the operating system by setting the environment variable PROLOGKEEPSIZE. Once Prolog grows to PROLOGKEEPSIZE bytes, it will always keep at least PROLOGKEEPSIZE bytes around. Only memory that was allocated above and beyond PROLOGKEEPSIZE is returned to the OS. The values for PROLOGKEEPSIZE are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. For example, one of the following commands, depending upon which shell is being used, can be used to set PROLOGKEEPSIZE to 250 kilobytes:
setenv PROLOGKEEPSIZE 250K
or
PROLOGKEEPSIZE=250K; export PROLOGKEEPSIZE
The environment variable PROLOGLOCALMIN can be used to control the amount of space Prolog reserves for the local stack. The purpose of the local stack is described in detail in {manual(g-12)}. The values for PROLOGLOCALMIN are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. For example, one of the following commands, depending upon which shell is being used, can be used to set PROLOGINCSIZE to 250 kilobytes:
setenv PROLOGLOCALMIN 250K
or
PROLOGLOCALMIN=250K; export PROLOGLOCALMIN
By default PROLOGLOCALMIN is 64k.
The environment variable PROLOGGLOBALMIN can be used to control the amount of space Prolog reserves for the global stack. The purpose of the global stack is described in detail in {manual(g-12)}. The values for PROLOGGLOBALMIN are entered in bytes, but may be followed by K or M meaning kilobytes or megabytes respectively. For example, one of the following commands, depending upon which shell is being used, can be used to set PROLOGINCSIZE to 250 kilobytes:
setenv PROLOGGLOBALMIN 250K
or
PROLOGGLOBALMIN=250K; export PROLOGGLOBALMIN
By default PROLOGGLOBALMIN is 64k.
By default, atoms created during the execution of a program remain permanently in the system until Prolog exits. For the majority of applications this behavior is not a problem and can be ignored. However, for two classes of application this can present problems. Firstly the internal architecture of Quintus Prolog limits the number of atoms that can be created to 2,031,616 and this can be a problem for database applications that read large numbers of atoms from a database. Secondly, the space occupied by atoms can become significant and dominant memory usage, which can be a problem for processes designed to run perpetually. These problems can be overcome by using atom garbage collection to reclaim atoms that are no longer accessible to the executing program. Atoms can be created in many ways: when an appropriate token is read with read_term/3, when source or QOF files are loaded, when atom_chars/2 is called with a character list, or when QP_atom_from_string() is called in C code. In any of these contexts an atom is only created if it does not already exist; all atoms for a given string are given the same identification number, which is different from the atom of any other string. Thus, atom recognition and comparison can be done quickly, without having to look at strings. An occurrence of an atom is always of a fixed, small size, so where a given atom is likely to be used in several places simultaneously the use of atoms can also be more compact than the use of strings. A Prolog functor is implemented like an atom, but also has an associated arity. For the purposes of atom garbage collection, a functor is considered to be an occurrence of the atom of that same name. Atom garbage collection is similar to heap garbage collection except that it is not invoked automatically, but rather through a call to the built-in predicate garbage_collect_atoms/0. The atom garbage collector scans Prolog's data areas looking for atoms that are currently in use and then throws away all unused atoms, reclaiming their space. Atom garbage collection can turn an application that continually grows and eventually either runs into the atom number limit or runs out of space into one that can run perpetually. It can also make feasible applications that load and manipulate huge quantities of atom-rich data that would otherwise become full of useless atoms.
Because the creation of atoms does not follow any other system behaviors like memory growth or heap garbage collection, Quintus has chosen to keep the invocation of atom garbage collection independent of any other operation and to keep the invocation of atom garbage collection explicit rather than making it automatic. It is often preferable for the programmer to control when it will occur in case preparations need to be made for it. Atom garbage collection is invoked by calling the new built-in predicate garbage_collect_atoms/0. The predicate normally succeeds silently. The user may determine whether to invoke atom garbage collection at a given point based on information returned from a call to statistics/2 with the keyword atoms. That call returns a list of the form
[number of atoms, atom space in use, atom space free]
For example,
| ?- statistics(atoms, Stats). Stats = [4313,121062,31032]
One would typically choose to call garbage_collect_atoms/0 prior to each iteration of an iterative application, when either the number of atoms or the atom space in use passes some threshold, e.g.
<driver loop> :-
...
repeat,
maybe_atom_gc,
<do next iteration>
...
fail.
<driver loop>.
where
maybe_atom_gc :-
statistics(atoms, [_,Inuse,_]),
atom_gc_space_threshold(Space),
( Inuse > Space -> garbage_collect_atoms ; true ).
% Atom GC if there are more than 100000 bytes of atoms:
atom_gc_space_threshold(100000).
More sophisticated approaches might use both atom number and atom space thresholds, or could adjust a threshold if atom garbage collection didn't free an adequate number of atoms. To be most effective, atom garbage collection should be called when as few as possible atoms are actually in use. In the above example, for instance, it makes the most sense to do atom garbage collection at the beginning of each iteration rather than at the end, as at the beginning of the iteration the previous failure may just have freed large amounts of atom-rich global and local stack. Similarly, it's better to invoke atom garbage collection after abolishing or retracting a large database than to do so before.
Quintus Prolog's foreign language interface allows atoms to be passed to foreign functions. When calling foreign functions from Prolog, atoms are passed via the +atom argument type in the predicate specifications of foreign/[2,3] facts. The strings of atoms can be passed to foreign functions via the +string argument type. In the latter case a pointer to the Prolog symbol table's copy of the string for an atom is what is passed. When calling Prolog from C, atoms are passed back from C to Prolog using the -atom and -string argument types in extern/1 declarations. Atoms can also be created in foreign code via functions like QP_atom_from_string(). Prolog does not keep track of atoms (or strings of atoms) stored in foreign memory. As such, it cannot guarantee that those atoms will be retained by atom garbage collection. Therefore Quintus Prolog provides functions to to register atoms (or their strings) with the atom garbage collector. Registered atoms will not be reclaimed by the atom garbage collector. Atoms can be registered while it is undesirable for them to be reclaimed, and then unregistered when they are no longer needed. Of course, the majority of atoms passed as atoms or strings to foreign functions do not need to be registered. Only those that will be stored across foreign function calls (in global variables) or across nested calls to Prolog are at risk. An extra margin of control is given by the fact the programmer always invokes atom garbage collection explicitly, and can ensure that this is only done in contexts that are "safe" for the individual application. To register or unregister an atom, one of the following functions is used:
int QP_register_atom(atom) QP_atom atom; int QP_unregister_atom(atom) QP_atom atom;
These functions return either QP_ERROR or a non-negative integer. The return values are discussed further in {manual(g-12-8-4)}. As noted above, when an atom is passed as a string (+string) to a foreign function, the string the foreign function receives is the one in Prolog's symbol table. When atom garbage collection reclaims the atom for that string, the space for the string will also be reclaimed. Thus, if the string is to be stored across foreign calls then either a copy of the string or else the atom (+atom) should be passed into the foreign function so that it can be registered and QP_string_from_atom() can be used to access the string from the atom. Keep in mind that the registration of atoms only pertains to those passed to foreign functions or created in foreign code. Atoms in Prolog's data areas are maintained automatically. Note also that even though an atom may be unregistered in foreign code, atom garbage collection still may not reclaim it as it may be referenced from Prolog's data areas. But if an atom is registered in foreign code, it will be preserved regardless of its presence in Prolog's data areas. The following example illustrates the use of these functions. In this example the current value of an object (which an atom) is being stored in a C global variable. There are two C functions that can be called from Prolog, one to update the current value and one to access the value.
#include <quintus/quintus.h>
QP_atom current_object = NULL;
update_object(newvalue)
QP_atom newvalue;
{
/* if current_object contains an atom, unregister it */
if (current_object)
(void) QP_unregister_atom(current_object);
/* register new value */
(void) QP_register_atom(newvalue);
current_object = newvalue;
}
QP_atom get_object()
{
return current_object;
}
Atom garbage collection scans all Prolog's dynamic data areas when looking for atoms that are in use. Scanning finds atoms in the Prolog stacks and in all compiled and interpreted code that has been dynamically loaded into Prolog via consult/1, use_module/1, assert/2, etc. However, there are certain potential sources of atoms in the Prolog image from which atoms cannot be reclaimed. Atoms for Prolog code that has been statically linked with either the Prolog Development Environment or the Runtime Environment have been placed in the UNIX text space, making them (and the code that contains them) effectively permanent. Although such code can be abolished, its space can never be reclaimed. These atoms are internally flagged as permanent by the system and are always retained by atom garbage collection. An atom that has become permanent cannot be made non-permanent, so can never be reclaimed.
The functions that register and unregister atoms are in fact using reference counting to keep track of atoms that have been registered. As a result, it is safe to combine your code with libraries and code others have written. If the other code has been careful to register and unregister its atoms as appropriate, atoms will not be reclaimed until everyone has unregistered them. Of course, it is possible when writing code that needs to register atoms that errors could occur. Atoms that are registered too many times simply will not be garbage collected until they are fully unregistered. However, atoms that aren't registered when they should be may be reclaimed on atom garbage collection. One normally doesn't need to think about the reference counting going on in QP_register_atom() and QP_unregister_atom(), but some understanding of its details could prove helpful when debugging. To help you diagnose problems with registering and unregistering atoms, QP_register_atom() and QP_unregister_atom() both normally return the current reference count for the atom. If an error occurs, e.g. a nonexistent atom is registered or unregistered, QP_ERROR is returned. An unregistered atom has a reference count of 0. Unregistering an atom that is unregistered is a no-op; in this case, QP_unregister_atom() returns 0. A permanent atom has a reference count of 128. In addition, if an atom is simultaneously registered 128 times, it becomes permanent. (An atom with 128 distinct references is an unlikely candidate for reclamation!) Registering or unregistering an atom that is permanent is also a no-op; QP_register_atom() and QP_unregister_atom() return 128. Various safeguards enable you to detect when an atom may have been reclaimed prematurely. An atom that has been reclaimed and has not yet been reused appears as the special system atom '$anon', which cannot match any user atom (even a user-supplied '$anon', which will be a distinct atom). However, once an atom's space is reused, any references to the old atom will now see only the new atom. It is not possible to detect that an atom has been reused once the reuse occurs.
contact: product
support sales information