# Copyright: 2001-2004 The Perl Foundation.  All Rights Reserved.
# $Id: pmc_freeze.pod 5917 2004-04-23 02:22:51Z scog $

=head1 NAME

docs/dev/pmc_freeze.pod - Freeze/Thaw Design Notes

=head1 VERSION

This document describes freeze/thaw internals version 0.1. This is not
the final implementation.

=head1 Overview

Freezing or serializing arbitrary PMCs is an interesting problem.
Aggregates can hold other aggregates and can be deeply nested, so
so a recursive approach could easily blow the stack, especially on embedded
systems. Also, aggregates can be self-referential -- they can hold
pointers to themselves -- so that working on such structures
could create infinite loops.

=head1 Coverage

Although the file is named F<pmc_freeze.c> it ultimately will deal with 
every kind of operation that deeply traverses an arbitrary data structures.
For example:

=over 4

=item freeze

Called from user code to serialize the state of a PMC into some
(possibly binary) representation held in a STRING.

=item freeze_at_destruct

A variant of C<freeze>, possibly called from an exception handler or on
resource shortage before interpreter shutdown, to save some data
before dying. It must not consume any additional resources.

=item thaw

The opposite of C<freeze>: reconstruct all PMCs to generate an identical
copy of the original frozen PMC. As with C<freeze>, can be called from user 
code.

=item dclone

Deeply clone an aggregate. C<dclone(p)> is basically the same as 
C<thaw(freeze(p))>.

=item dump, pretty_print

Create a visual representation of an aggregate.

=item destruction ordering

Find the logical dependencies of a collection of PMCs, so that they can be 
destroyed in an appropriate order. This is also called on interpreter 
shutdown.

=item mark

Mark all objects as being live by calling B<pobject_lives> called from
DOD. While the functionality is the same, it will not be implemented
on top of this general scheme for performance reasons. This leads to
some code duplication, but DOD is run permanently and deserves all the
speed it can get.

=back

=head1 Description

The basic scheme of operation looks like this:

  info = init()
  push todo_list, pmc
  while (todo_list)
      current = shift todo_list
      current->visit(info)
  done.

=head2 The visit_info structure

This structure holds all necessary information and function pointers
specific to the desired functionality. It gets passed on to all vtable
methods and callback functions.

=head2 Working loops

These are labeled B<visit_loop_*>. There are currently two schemes to
handle the B<todo_list>.

=over 4

=item next_for_GC

All PMCs that can contain other PMCs have the B<next_for_GC> pointer
in the PMC's extended data area. The B<todo_list> gets built by
appending (or prepending) the current PMC to a B<mark_ptr>, which then
points to the current PMC, forming a linked list of items.

This pointer is also used during DOD's B<mark()> functionality, so
that DOD has to be turned off during operations using this scheme.

As the B<next_for_GC> pointer is inside the PMC, this scheme isn't
thread-safe at low-level, because shared PMCs also would share this
pointer, so that there can be only one operation at a time.

=item todo list

A B<List> called B<todo> holds items still to be worked on. This
method is slower and consumes more resources, but doesn't interfere with
DOD runs and is thread-safe.

=back

=head2 Putting items on the todo list

This is done by a callback function inside the B<visit_info> structure
called B<visit_pmc_now>. It gets called initially to put the
first item on the list and is called thereafter from all PMCs for
contained PMCs inside the B<visit> vtable method.

There is another callback B<visit_pmc_later> which adds PMCs to the
todo list for later processing, but doesn't do any action on these
immediately.


=head2 The visit() vtable

The general scheme above shows that this method is called for all
items on the B<todo_list>. B<visit> has to call
B<visit_pmc_now> for all contained PMCs, which then get
visited until all is done.

=head2 The visit_pmc_now() callback

The basic operation is:

  (seen, id) = was_already_seen(pmc)
  do_specific_action(pmc, seen, id)
  if (!seen)
     pmc->visit_action()

=head2 Avoiding duplicates

As stated in the introduction structures can be self-referential,
they can contain (at an arbitrary depth) PMCs, that were already
processed. Just following these PMCs would lead to endless loops. So
already B<seen> PMCs have to be remembered.

=over 4

=item The B<seen> hash

Using a B<Hash> is one method to avoid duplicates. The B<seen> hash
holds keys being the address of the PMC and values being a PMC B<id>,
which is unique for this PMC. While this is straight forward, it consumes
16 bytes per PMC (plus overhead, 32-bit system assumed). Hash lookups
also take a considerable amount of time.

=item B<next_for_GC>

The pointer used for the B<todo_list> handling itself can serve as a
marker that this item was already processed. There are some issues
with this though: Plain scalars (not being able to contain other PMCs)
don't have a B<next_for_GC> pointer. This is an optimization reducing
the size of scalars and increasing performance considerably.

Second, the B<next_for_GC> pointers have to be cleared beforehand. DOD
uses only a nibble-sized flag area located inside the PMCs arena to
manage, if a PMC was seen already by checking the live bit. The
B<next_for_GC> pointer is just set and never cleared to avoid touching
a PMCs memory and polluting caches when possible.

Finally, generating a PMC's B<id> isn't as simple as just incrementing
a counter used with the B<seen> hash approach.

=item PMC B<id>s

We could of course use the PMC's address as its own B<id>, since we know
it is unique. However, this is suboptimal for thawing.
To manage duplicates during B<thaw> we basically need a mapping
B<PMC_in_image =E<gt> newly_constructed_PMC>. When now the
B<PMC_in_image> (the B<id>) is the address, we have to use a hash
again, for B<thaw()> with all the negative impact on resources and
speed.

So both schemes are using small B<id> values and the seen handling
inside B<thaw> is done via a list lookup, which is a lot faster and
takes less resources.

The B<seen> hash approach just has a counter for PMC B<id>s, the
B<next_for_GC> approach calculates the B<id> from the address of the
PMC in its arena, again yielding a small and unique number. The two low
bits of PMC B<id>s are used as flags.

=back

=head2 The actual action

So after all we finally arrived at the point to actually perform the
desired functionality. First the PMC-specific part is done inside
F<pmc_freeze.c> then the specific vtable method B<freeze>, B<thaw>,
whatever, is called, again via a function pointer called
B<visit_action>.

=head1 Freeze and thaw

As stated PMCs are currently processed inside the core, PMC-specific
parts are done by calling the PMCs vtable method. This parts could of
course be moved to F<default.pmc> too, so that it's simpler to override
the functionality.

=head2 Serializer interface

During initialization the B<visit_info>s B<image_io> data pointer is
filled with an object having B<vtable> methods that remarkably look
like a PMCs vtable. So B<io-E<gt>vtable-E<gt>push_integer> spits out
an INTVAL to the frozen B<image>, while B<shift_integer> gets an
INTVAL from the frozen stream.

This simplifies final changes when B<image_io> becomes just a PMC of some
serializer class. There are currently two serializers:

=over 4

=item Plain text

This serializer is mainly intended for testing. Having a readable
representation of the image simplifies debugging a lot.

=item Parrot Byte Code

We already have a platform-independent way of reading and writing
opcodes, string-, and number-constants. So this serializer uses
functionality of the pack-file routines. The produced image isn't as
dense as it could be though, because all data are aligned at
B<opcode_t> boundaries.

=back

=head2 Image data format

PMC B<id>s ranging from 1 to N-PMCs are shifted left by two, so that
the 2 lo bits can serve as flags:

  id + 0x1   ... PMC was seen
  id + 0x2   ... PMC has same type as previous PMC
  id + 0x3   ... escape flag

A PMCs image generally looks like:

  <id><type><pmc-specific-data>

The text representation of the array

  P0 = [P1=666, P2=777, P0]

may look like:

  0xdf4 30 3 0xdf8 33 666 0xdf2 777 0xdf5

  0xdf4 ... PMC id (with "0x" in front for clarity)
  30    ... enum_class_PerlArray
  3     ... elements count
  0xdf8 ... id of first element
  33    ... enum_class_PerlInt
  666   ... value
  0xdf2 ... id of second element, same type as prev element
  777   ... value
  0xdf5 ... id of array itself with lo bit set

The escape flag marks places in the image, where additional data will
follow. After the escape flag is an int defining the kind of the
following data, passed on in B<extra_flags>.  During B<thaw> the PMCs
vtable is called again, to restore these data. So a PMCs B<thaw>
vtable has to check B<extra_flags> if normal or extra data have to be
shifted from the image.

This is e.g. needed for PMC properties or arrays containing sparse
holes, to set the array index of the following data.

A PerlInt(666) with a property hash ("answer"=>42) thus looks like:

  0xdfc 33 666 0xdff 2 0xdf4 32 1 answer 0xdf8 33 42

B<0xdff> is the escape mark for the PMC B<0xdfc> followed by the
constant B<EXTRA_IS_PROP_HASH>.

[ To be continued ]

=head1 FILES

F<src/pmc_freeze.c>, F<pf/pf_items.c>

=head1 Author

Leopold Toetsch <lt@toetsch.at>

=cut

# vim: expandtab shiftwidth=2 tw=70:

