A GlobalManager
object does some very simple management of certain
global values used by CoCoALib. You must create exactly one object of
type GlobalManager
before using any other feature of CoCoALib.
Conversely, the GlobalManager
object must be destroyed only after
you have completely finished using CoCoALib values and operations. An easy
way to achieve this is to create a local variable of type GlobalManager
at the start of a top level procedure (e.g. main
) -- see the
CoCoALib example programs listed above.
Shortcut: most likely you will want to use one of the following at the start of your top-level procedure:
GlobalManager CoCoAFoundations; // use default settings GlobalManager CoCoAFoundations(UseNonNegResidues); // printing preference GlobalManager CoCoAFoundations(UseGMPAllocator); // faster but NOT THREADSAFE!
Note about threadsafety the ctor for GlobalManager
is not
threadsafe; it is the user's responsibility to avoid trying to create
several instances simultaneously.
The ctor for a GlobalManager
has one (optional) argument. This
argument is used to specify the global settings, namely
The current defaults are to use the system memory mananger and symmetric residues.
CoCoALib BigInt
values are implemented using the GMP library which
needs to have access to a memory manager. There are three possibilities
for specifying the memory manager for GMP:
UseSystemAllocatorForGMP
(default) to use the system memory manager (i.e. malloc
)
UseGMPAllocator
to use the CoCoALib custom memory manager
UseGMPAllocator(sz)
to use the CoCoALib custom memory manager with a slice size of sz bytes
WARNING if your program is multi-threaded or if you store GMP values in global variables or if your program uses another library which depends on GMP, then it is safest to use only the system memory manager!
Nevertheless, the CoCoALib custom allocator offers slightly better performance, and may be helpful when debugging or fine-tuning code.
CoCoALib lets you choose between two conventions for printing elements of rings of the form ZZ/m:
UseSymmResidues
(default) symmetric residues (if m is even, the residue m/2 is printed as positive)
UseNonNegResidues
least non-negative residues (i.e. from 0 to m-1)
You may ask CoCoALib which convention has been chosen
using DefaultResiduesAreSymm()
see GlobalManager
operations below.
To specify more than one global setting the individual specifiers should be
combined using operator+
, like this:
GlobalManager CoCoAFoundations(UseNonNegResidues + UseGMPAllocator);
Combining incompatible or redundant specifiers will produce a run-time
error: an exception of type CoCoA::ErrorInfo
having error
code
ERR::BadGlobalSettings
.
Similarly an exception will be thrown if you attempt to create more than one
live GlobalManager
object. The exception is of type CoCoA::ErrorInfo
and has error
code ERR::GlobalManager2
.
Once the GlobalManager
has been created you can use the following functions:
DefaultResiduesAreSymm()
-- returns true
iff the convention is
UseSymmResidues
.
GlobalRandomSource()
-- returns a global randomness source;
see RandomSource
for a description of the permitted operations
on random source objects.
The concept of GlobalManager
was created to handle in a clean and
coherent manner (almost) all global values used by CoCoALib; in particular
it was prompted by the decision to make the ring of integers a global value
(and also the field of rationals). The tricky part was ensuring the
orderly destruction of RingZZ
and RingQQ
before main
exits.
Recall that C++ normally destroys globals after main
has completed, and
that the order of destruction of globals cannot easily be governed;
destroying values in the wrong order can cause to the program to crash just
before it terminates. Another advantage of forcing destruction before
main
exits is that it makes debugging very much simpler (e.g. the
MemPool
object inside RingZZImpl
will be destroyed while the input
and output streams are still functioning, thus allowing the MemPool
destructor to report any anomalies). And of course, it is simply good
manners to clean up properly at the end of the program.
To implement the restriction that only one GlobalManager
may exist
at any one time, the first instruction in the ctor checks that the
global variable GlobalManager::ourGlobalDataPtr
is null. If it is
null, it is immediately set to point the object being constructed. At
the moment, this check is not threadsafe.
The ctor for GlobalManager
is fairly delicate: e.g. the functions
it calls cannot use the functions RingZZ()
and RingQQ()
since
they will not work before the GlobalManager
is registered.
The two functions MakeUniqueCopyOfRingZZ
and MakeUniqueCopyOfRingQQ
are supposed to be accessible only to the ctor of GlobalManager
; they
create the unique copies of those two rings which will be stored in the
global data. The functions are defined in RingZZ.C
and RingQQ.C
respectively but do not appear in the corresponding header files (thus
making them "invisible" to other users).
The dtor for GlobalManager
checks that RingZZ
and RingQQ
are not
referred to by any other values (e.g. ring elements which have been
stored in global variables). A rude message is printed on cerr
if
the reference counts are too high, and a program crash is likely once
the GlobalManager
has been destroyed.
The GMPMemMgr
class performs the necessary steps for setting the
memory manager for GMP values. At the moment there are essentially
two choices: use the system memory manager, or use a MemPool
to handle
the memory for small values. The first parameter to the ctor for
GMPMemMgr
says which sort of memory manager to use. If the system
allocator is chosen, then the ctor does nothing (since the GMP default
is the system manager); similarly nothing is done when the GMPMemMgr
object is destroyed. The second argument is completely ignored when
the system allocator is chosen.
The situation is more complicated if CoCoALib's custom allocator is to
be used. The second argument specifies the slice size (in bytes)
which is to be used -- the implementation may automatically increase
this value to the next convenient value (see also the documentation
for MemPool
). The slice size defines what a GMP small value is:
it is a value whose GMP internal representation fits into a single slice.
The memory for small values is managed by a (global) MemPool
, while
the memory for larger values is managed by the standard malloc
family
of functions.
Since the only place a GMPMemMgr
object appears is as a data field in a
GlobalManager
, we have an automatic guarantee that there will be at
most one GMPMemMgr
object in existence -- this fact is exploited
(implicitly) in the ctor and dtor for GMPMemMgr
when calling the GMP
functions for setting the memory management functions.
Of the alloc/free/realloc
functions which are handed to GMP, only
CoCoA_GMP_realloc
displays any complication. GMP limbs can be stored
either in memory supplied by the MemPool
belonging to a GMPAllocator
object or in system allocated memory; a reallocation could cause the limbs
to be moved from one sort of memory to the other.
The GlobalSettings
class serves only to allow a convenient syntax for
specifying the parameters to the GlobalManager
ctor. The only mild
complication is the operator+
for combining the ctor parameters, where
we must check that nonsensical or ambiguous combinations are not built.
2010-09-30 The private copies of RingZZ
and RingQQ
are now direct
members, previously they were owned via auto_ptr
s. The new
implementation feels cleaner, but has to include the definitions of
ring
and FractionField
in the header file.
You cannot print out a GlobalManager
object; is this really a bug?
Ctor for GlobalManager
is NOT THREADSAFE.
Should the ctor for GlobalManager
set the globals which control
debugging and verbosity in MemPool
s?