NAME ^

src/pmc_freeze.c - Freeze and thaw functionality

DESCRIPTION ^

Freeze uses the next_for_GC pointer() to remember seen PMCs. PMCs are written as IDs (or tags), which are calculated from their arena address. This PMC number is multiplied by four. The 2 low bits indicate a seen PMC or a PMC of the same type as the previous one respectively.

Thawing PMCs uses a list with (maximum) size of the amount of PMCs to keep track of retrieved PMCs.

The individual information of PMCs is frozen/thawed by their vtables.

To avoid recursion, the whole functionality is driven by pmc->vtable->visit, which is called for the first PMC initially. Container PMCs call a "todo-callback" for all contained PMCs. The individual action vtable (freeze/thaw) is then called for all todo-PMCs.

In the current implementation IMAGE_IO is a stand-in for some kind of serializer PMC which will eventually be written. It associates a Parrot STRING with a vtable.

Image Stream Functions ^

static void str_append(PARROT_INTERP, NOTNULL(STRING *s), NOTNULL(const void *b), size_t len)

Appends len bytes from buffer *b to string *s.

Plain ascii - for testing only:

For speed reasons we mess around with the string buffers directly.

No encoding of strings, no transcoding.

static void push_ascii_integer(PARROT_INTERP, NOTNULL(IMAGE_IO *io), INTVAL v)

Pushes an ASCII version of the integer v onto the end of the *io "stream".

static void push_ascii_number(PARROT_INTERP, NOTNULL(IMAGE_IO *io), FLOATVAL v)

Pushes an ASCII version of the number v onto the end of the *io "stream".

static void push_ascii_string(PARROT_INTERP, NOTNULL(IMAGE_IO *io), NOTNULL(STRING *s))

Pushes an ASCII version of the string *s onto the end of the *io "stream".

For testing only - no encodings and such.

XXX no string delimiters - so no space allowed.

static void push_ascii_pmc(PARROT_INTERP, NOTNULL(IMAGE_IO *io), NOTNULL(const PMC *v))

Pushes an ASCII version of the PMC *v onto the end of the *io "stream".

static INTVAL shift_ascii_integer(SHIM_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an integer from the start of the *io "stream".

static FLOATVAL shift_ascii_number(SHIM_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an number from the start of the *io "stream".

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static STRING *shift_ascii_string(PARROT_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an string from the start of the *io "stream".

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static PMC *shift_ascii_pmc(SHIM_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns a PMC from the start of the *io "stream".

opcode_t IO Functions ^

PARROT_INLINE static void op_check_size(PARROT_INTERP, NOTNULL(STRING *s), size_t len)

Checks the size of the "stream" buffer to see if it can accommodate len more bytes. If not then the buffer is expanded.

static void op_append(PARROT_INTERP, NOTNULL(STRING *s), opcode_t b, size_t len)

Appends the opcode b to the string *s.

static void push_opcode_integer(PARROT_INTERP, NOTNULL(IMAGE_IO *io), INTVAL v)

Pushes the integer v onto the end of the *io "stream".

XXX assumes sizeof (opcode_t) == sizeof (INTVAL).

static void push_opcode_number(PARROT_INTERP, NOTNULL(IMAGE_IO *io), FLOATVAL v)

Pushes the number v onto the end of the *io "stream".

static void push_opcode_string(PARROT_INTERP, NOTNULL(IMAGE_IO *io), NOTNULL(STRING *v))

Pushes the string *v onto the end of the *io "stream".

static void push_opcode_pmc(PARROT_INTERP, NOTNULL(IMAGE_IO *io), NOTNULL(PMC *v))

Pushes the PMC *v onto the end of the *io "stream".

static INTVAL shift_opcode_integer(SHIM_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an integer from the start of the *io "stream".

TODO - The shift functions aren't portable yet. We need to have a packfile header for wordsize and endianess.

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static PMC *shift_opcode_pmc(PARROT_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an PMC from the start of the *io "stream".

Note that this actually reads a PMC id, not a PMC.

static FLOATVAL shift_opcode_number(SHIM_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns an number from the start of the *io "stream".

PARROT_WARN_UNUSED_RESULT PARROT_CANNOT_RETURN_NULL static STRING *shift_opcode_string(PARROT_INTERP, NOTNULL(IMAGE_IO *io))

Removes and returns a string from the start of the *io "stream".

Helper Functions ^

static void pmc_add_ext(PARROT_INTERP, NOTNULL(PMC *pmc))

Adds a PMC_EXT to *pmc.

static void cleanup_next_for_GC_pool(NOTNULL(Small_Object_Pool *pool))

Sets all the next_for_GC pointers to NULL.

static void cleanup_next_for_GC(PARROT_INTERP)

Cleans up the next_for_GC pointers.

static void ft_init(PARROT_INTERP, NOTNULL(visit_info *info))

Initializes the freeze/thaw subsystem.

static void todo_list_init(PARROT_INTERP, NOTNULL(visit_info *info))

Initializes the *info lists.

PARROT_INLINE static void freeze_pmc(PARROT_INTERP, NULLOK(PMC *pmc), NOTNULL(visit_info *info), int seen, UINTVAL id)

TODO: Not yet documented!!!

PARROT_INLINE static int thaw_pmc(PARROT_INTERP, NOTNULL(visit_info *info), NOTNULL(UINTVAL *id), NOTNULL(INTVAL *type))

Freeze and thaw a PMC (id).

For example, the ASCII representation of the Array

    P0 = [P1=666, P2=777, P0]
may look like this:

    0xdf4 30 3 0xdf8 33 666 0xdf2 777 0xdf5
where 30 is class_enum_Array, 33 is class_enum_Integer, the type of the second Integer is suppressed, the repeated P0 has bit 0 set.

PARROT_INLINE static void do_action(PARROT_INTERP, NULLOK(PMC *pmc), NOTNULL(visit_info *info), int seen, UINTVAL id)

Called from visit_next_for_GC() and visit_todo_list() to perform the action specified in info->what.

Currently only VISIT_FREEZE_NORMAL is implemented.

PARROT_INLINE PARROT_CANNOT_RETURN_NULL static PMC *thaw_create_pmc(PARROT_INTERP, NOTNULL(const visit_info *info), INTVAL type)

Called from do_thaw() to attach the vtable etc. to *pmc.

PARROT_INLINE static void do_thaw(PARROT_INTERP, NOTNULL(PMC *pmc), NOTNULL(visit_info *info))

Called by visit_todo_list_thaw() to thaw and return a PMC.

seen is false if this is the first time the PMC has been encountered.

static UINTVAL id_from_pmc(PARROT_INTERP, NOTNULL(PMC *pmc))

TODO: Not yet documented!!!

static void add_pmc_next_for_GC(SHIM_INTERP, NOTNULL(PMC *pmc), NOTNULL(visit_info *info))

Remembers the PMC for later processing.

PARROT_INLINE static int next_for_GC_seen(PARROT_INTERP, NULLOK(PMC *pmc), NOTNULL(visit_info *info), NOTNULL(UINTVAL *id))

Remembers next child to visit via the next_for_GC pointer generate a unique ID per PMC and freeze the ID (not the PMC address) so thaw the hash-lookup can be replaced by an array lookup then which is a lot faster.

static void add_pmc_todo_list(PARROT_INTERP, NULLOK(PMC *pmc), NOTNULL(visit_info *info))

Remembers the PMC to be processed later.

PARROT_INLINE static int todo_list_seen(PARROT_INTERP, NOTNULL(PMC *pmc), NOTNULL(visit_info *info), NOTNULL(UINTVAL *id))

Returns true if the PMC was seen, otherwise it put it on the todo list, generates an ID (tag) for PMC, offset by 4 as are addresses, low bits are flags.

static void visit_next_for_GC(PARROT_INTERP, NOTNULL(PMC *pmc), NOTNULL(visit_info *info))

visit_child callbacks:

Checks if the PMC was seen, generate an ID for it if not, then do the appropriate action.

static void visit_todo_list(PARROT_INTERP, NOTNULL(PMC *pmc), NOTNULL(visit_info *info))

Checks the seen PMC via the todo list.

static void visit_todo_list_thaw(PARROT_INTERP, NOTNULL(PMC *old), NOTNULL(visit_info *info))

Callback for thaw - action first.

Todo-list and seen handling is all in do_thaw().

static void visit_loop_next_for_GC(PARROT_INTERP, NOTNULL(PMC *current), NOTNULL(visit_info *info))

Put first item on todo list, then run as long as there are items to be done.

static void visit_loop_todo_list(PARROT_INTERP, NULLOK(PMC *current), NOTNULL(visit_info *info))

The thaw loop.

static void create_image(PARROT_INTERP, NULLOK(PMC *pmc), NOTNULL(visit_info *info))

Allocate image to some estimated size.

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static PMC *run_thaw(PARROT_INTERP, NOTNULL(STRING *image), visit_enum_type what)

Performs thawing. what indicates what to be thawed.

Thaw could use the next_for_GC pointers as todo-list too, but this would need 2 runs through the arenas to clean the next_for_GC pointers.

For now it seems cheaper to use a list for remembering contained aggregates. We could of course decide dynamically, which strategy to use, e.g.: given a big image, the first thawed item is a small aggregate. This implies, it probably contains (or some big strings) more nested containers, for which the next_for_GC approach could be a win.

Public Interface ^

PARROT_API PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL STRING *Parrot_freeze_at_destruct(PARROT_INTERP, NOTNULL(PMC *pmc))

This function must not consume any resources (except the image itself). It uses the next_for_GC pointer, so its not reentrant and must not be interrupted by a DOD run.

PARROT_API PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL STRING *Parrot_freeze(PARROT_INTERP, NOTNULL(PMC *pmc))

Freeze using either method.

PARROT_API PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL PMC *Parrot_thaw(PARROT_INTERP, NOTNULL(STRING *image))

Thaw a PMC, called from the thaw opcode.

PARROT_API PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL PMC *Parrot_thaw_constants(PARROT_INTERP, NOTNULL(STRING *image))

Thaw the constants. This is used by PackFile for unpacking PMC constants.

PARROT_API PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL PMC *Parrot_clone(PARROT_INTERP, NOTNULL(PMC *pmc))

There are for sure shortcuts to clone faster, e.g. always thaw the image immediately or use a special callback. But for now we just thaw a frozen PMC.

TODO ^

The seen-hash version for freezing might go away sometimes.

SEE ALSO ^

Lot of discussion on p6i and docs/dev/pmc_freeze.pod.

HISTORY ^

Initial version by leo 2003.11.03 - 2003.11.07.


parrot