2 Copyright (C) 2001-2006, The Perl Foundation.
7 src/pmc_freeze.c - Freeze and thaw functionality
11 Freeze uses the C<next_for_GC pointer()> to remember seen PMCs. PMCs are
12 written as IDs (or tags), which are calculated from their arena address.
13 This PMC number is multiplied by four. The 2 low bits indicate a seen
14 PMC or a PMC of the same type as the previous one respectively.
16 Thawing PMCs uses a list with (maximum) size of the amount of PMCs to
17 keep track of retrieved PMCs.
19 The individual information of PMCs is frozen/thawed by their vtables.
21 To avoid recursion, the whole functionality is driven by
22 C<< pmc->vtable->visit >>, which is called for the first PMC initially.
23 Container PMCs call a "todo-callback" for all contained PMCs. The
24 individual action vtable (freeze/thaw) is then called for all todo-PMCs.
26 In the current implementation C<IMAGE_IO> is a stand-in for some kind of
27 serializer PMC which will eventually be written. It associates a Parrot
28 C<STRING> with a vtable.
34 #include "parrot/parrot.h"
37 /* default.pmc thawing of properties */
38 void Parrot_default_thaw(Interp
* , PMC
* pmc
, visit_info
*info
);
41 * define this to 1 for testing
44 #define FREEZE_ASCII 0
48 * normal freeze can use next_for_GC ptrs or a seen hash
50 #define FREEZE_USE_NEXT_FOR_GC 0
53 * when thawing a string longer then this size, we first do a
54 * DOD run and then block DOD/GC - the system can't give us more headers
56 #define THAW_BLOCK_DOD_SIZE 100000
59 * preallocate freeze image for aggregates with this estimation
62 # define FREEZE_BYTES_PER_ITEM 17
64 # define FREEZE_BYTES_PER_ITEM 9
69 =head2 Image Stream Functions
74 str_append(Parrot_Interp interpreter, STRING *s, const void *b, size_t len)>
76 Appends C<len> bytes from buffer C<*b> to string C<*s>.
78 Plain ascii - for testing only:
80 For speed reasons we mess around with the string buffers directly.
82 No encoding of strings, no transcoding.
89 str_append(Parrot_Interp interpreter
, STRING
*s
, const void *b
, size_t len
)
91 const size_t used
= s
->bufused
;
92 const int need_free
= (int)PObj_buflen(s
) - used
- len
;
94 * grow by factor 1.5 or such
96 if (need_free
<= 16) {
97 size_t new_size
= (size_t) (PObj_buflen(s
) * 1.5);
98 if (new_size
< PObj_buflen(s
) - need_free
+ 512)
99 new_size
= PObj_buflen(s
) - need_free
+ 512;
100 Parrot_reallocate_string(interpreter
, s
, new_size
);
101 assert(PObj_buflen(s
) - used
- len
>= 15);
103 mem_sys_memcopy((void *)((ptrcast_t
)s
->strstart
+ used
), b
, len
);
111 push_ascii_integer(Parrot_Interp interpreter, IMAGE_IO *io, INTVAL v)>
113 Pushes an ASCII version of the integer C<v> onto the end of the C<*io>
121 push_ascii_integer(Parrot_Interp interpreter
, IMAGE_IO
*io
, INTVAL v
)
124 sprintf(buffer
, "%d ", (int) v
);
125 str_append(interpreter
, io
->image
, buffer
, strlen(buffer
));
131 push_ascii_number(Parrot_Interp interpreter, IMAGE_IO *io, FLOATVAL v)>
133 Pushes an ASCII version of the number C<v> onto the end of the C<*io>
141 push_ascii_number(Parrot_Interp interpreter
, IMAGE_IO
*io
, FLOATVAL v
)
144 sprintf(buffer
, "%g ", (double) v
);
145 str_append(interpreter
, io
->image
, buffer
, strlen(buffer
));
151 push_ascii_string(Parrot_Interp interpreter, IMAGE_IO *io, STRING *s)>
153 Pushes an ASCII version of the string C<*s> onto the end of the C<*io>
156 For testing only - no encodings and such.
158 XXX no string delimiters - so no space allowed.
165 push_ascii_string(Parrot_Interp interpreter
, IMAGE_IO
*io
, STRING
*s
)
167 const UINTVAL length
= string_length(interpreter
, s
);
168 char * const buffer
= malloc(4*length
);
169 char *cursor
= buffer
;
172 /* temporary--write out in UTF-8 */
173 for ( idx
= 0; idx
< length
; ++idx
) {
174 *cursor
++ = (unsigned char)string_index(interpreter
, s
, idx
);
177 str_append(interpreter
, io
->image
, buffer
, cursor
- buffer
);
178 str_append(interpreter
, io
->image
, " ", 1);
186 push_ascii_pmc(Parrot_Interp interpreter, IMAGE_IO *io, PMC* v)>
188 Pushes an ASCII version of the PMC C<*v> onto the end of the C<*io>
196 push_ascii_pmc(Parrot_Interp interpreter
, IMAGE_IO
*io
, const PMC
* v
)
199 sprintf(buffer
, "%p ", v
);
200 str_append(interpreter
, io
->image
, buffer
, strlen(buffer
));
205 =item C<static INTVAL
206 shift_ascii_integer(Parrot_Interp interpreter, IMAGE_IO *io)>
208 Removes and returns an integer from the start of the C<*io> "stream".
215 shift_ascii_integer(Parrot_Interp interpreter
, IMAGE_IO
*io
)
217 char * const start
= (char*)io
->image
->strstart
;
219 const INTVAL i
= strtoul(p
, &p
, 10);
222 assert(p
<= start
+ io
->image
->bufused
);
223 io
->image
->strstart
= p
;
224 io
->image
->bufused
-= (p
- start
);
225 assert((int)io
->image
->bufused
>= 0);
231 =item C<static FLOATVAL
232 shift_ascii_number(Parrot_Interp interpreter, IMAGE_IO *io)>
234 Removes and returns an number from the start of the C<*io> "stream".
241 shift_ascii_number(Parrot_Interp interpreter
, IMAGE_IO
*io
)
243 char * const start
= (char*)io
->image
->strstart
;
245 const FLOATVAL f
= (FLOATVAL
) strtod(p
, &p
);
248 assert(p
<= start
+ io
->image
->bufused
);
249 io
->image
->strstart
= p
;
250 io
->image
->bufused
-= (p
- start
);
251 assert((int)io
->image
->bufused
>= 0);
257 =item C<static STRING*
258 shift_ascii_string(Parrot_Interp interpreter, IMAGE_IO *io)>
260 Removes and returns an string from the start of the C<*io> "stream".
267 shift_ascii_string(Parrot_Interp interpreter
, IMAGE_IO
*io
)
271 char * const start
= (char*)io
->image
->strstart
;
277 assert(p
<= start
+ io
->image
->bufused
);
278 io
->image
->strstart
= p
;
279 io
->image
->bufused
-= (p
- start
);
280 assert((int)io
->image
->bufused
>= 0);
281 s
= string_make(interpreter
, start
, p
- start
- 1, "iso-8859-1", 0);
282 /* s = string_make(interpreter, start, p - start - 1, "UTF-8", 0); */
289 shift_ascii_pmc(Parrot_Interp interpreter, IMAGE_IO *io)>
291 Removes and returns a PMC from the start of the C<*io> "stream".
298 shift_ascii_pmc(Parrot_Interp interpreter
, IMAGE_IO
*io
)
300 char * const start
= (char*)io
->image
->strstart
;
302 const unsigned long i
= strtoul(p
, &p
, 16);
304 assert(p
<= start
+ io
->image
->bufused
);
305 io
->image
->strstart
= p
;
306 io
->image
->bufused
-= (p
- start
);
307 assert((int)io
->image
->bufused
>= 0);
315 =head2 C<opcode_t> IO Functions
319 =item C<static PARROT_INLINE void
320 op_check_size(Parrot_Interp interpreter, STRING *s, size_t len)>
322 Checks the size of the "stream" buffer to see if it can accommodate
323 C<len> more bytes. If not then the buffer is expanded.
329 static PARROT_INLINE
void
330 op_check_size(Parrot_Interp interpreter
, STRING
*s
, size_t len
)
332 const size_t used
= s
->bufused
;
333 const int need_free
= (int)PObj_buflen(s
) - used
- len
;
335 * grow by factor 1.5 or such
337 if (need_free
<= 16) {
338 size_t new_size
= (size_t) (PObj_buflen(s
) * 1.5);
339 if (new_size
< PObj_buflen(s
) - need_free
+ 512)
340 new_size
= PObj_buflen(s
) - need_free
+ 512;
341 Parrot_reallocate_string(interpreter
, s
, new_size
);
342 assert(PObj_buflen(s
) - used
- len
>= 15);
344 #ifndef DISABLE_GC_DEBUG
345 Parrot_go_collect(interpreter
);
352 op_append(Parrot_Interp interpreter, STRING *s, opcode_t b, size_t len)>
354 Appends the opcode C<b> to the string C<*s>.
361 op_append(Parrot_Interp interpreter
, STRING
*s
, opcode_t b
, size_t len
)
363 op_check_size(interpreter
, s
, len
);
364 *((opcode_t
*)((ptrcast_t
)s
->strstart
+ s
->bufused
)) = b
;
372 push_opcode_integer(Parrot_Interp interpreter, IMAGE_IO *io, INTVAL v)>
374 Pushes the integer C<v> onto the end of the C<*io> "stream".
376 XXX assumes sizeof(opcode_t) == sizeof(INTVAL).
383 push_opcode_integer(Parrot_Interp interpreter
, IMAGE_IO
*io
, INTVAL v
)
385 assert(sizeof(opcode_t
) == sizeof(INTVAL
));
386 op_append(interpreter
, io
->image
, (opcode_t
)v
, sizeof(opcode_t
));
392 push_opcode_number(Parrot_Interp interpreter, IMAGE_IO *io, FLOATVAL v)>
394 Pushes the number C<v> onto the end of the C<*io> "stream".
401 push_opcode_number(Parrot_Interp interpreter
, IMAGE_IO
*io
, FLOATVAL v
)
403 const size_t len
= PF_size_number() * sizeof(opcode_t
);
404 STRING
* const s
= io
->image
;
405 const size_t used
= s
->bufused
;
407 op_check_size(interpreter
, s
, len
);
408 PF_store_number( (opcode_t
*)((ptrcast_t
)s
->strstart
+ used
), &v
);
416 push_opcode_string(Parrot_Interp interpreter, IMAGE_IO *io, STRING* v)>
418 Pushes the string C<*v> onto the end of the C<*io> "stream".
425 push_opcode_string(Parrot_Interp interpreter
, IMAGE_IO
*io
, STRING
* v
)
427 const size_t len
= PF_size_string(v
) * sizeof(opcode_t
);
428 STRING
* const s
= io
->image
;
429 const size_t used
= s
->bufused
;
431 op_check_size(interpreter
, s
, len
);
432 PF_store_string( (opcode_t
*)((ptrcast_t
)s
->strstart
+ used
), v
);
440 push_opcode_pmc(Parrot_Interp interpreter, IMAGE_IO *io, PMC* v)>
442 Pushes the PMC C<*v> onto the end of the C<*io> "stream".
449 push_opcode_pmc(Parrot_Interp interpreter
, IMAGE_IO
*io
, PMC
* v
)
451 op_append(interpreter
, io
->image
, (opcode_t
)v
, sizeof(opcode_t
));
456 =item C<static INTVAL
457 shift_opcode_integer(Parrot_Interp interpreter, IMAGE_IO *io)>
459 Removes and returns an integer from the start of the C<*io> "stream".
461 TODO - The shift functions aren't portable yet. We need to have a
462 packfile header for wordsize and endianess.
469 shift_opcode_integer(Parrot_Interp interpreter
, IMAGE_IO
*io
)
471 const char * const start
= (char*)io
->image
->strstart
;
473 PF_fetch_integer(io
->pf
, (opcode_t
**) &io
->image
->strstart
);
475 io
->image
->bufused
-= ((char*)io
->image
->strstart
- start
);
476 assert((int)io
->image
->bufused
>= 0);
483 shift_opcode_pmc(Parrot_Interp interpreter, IMAGE_IO *io)>
485 Removes and returns an PMC from the start of the C<*io> "stream".
487 Note that this actually reads a PMC id, not a PMC.
494 shift_opcode_pmc(Parrot_Interp interpreter
, IMAGE_IO
*io
)
496 return (PMC
*) shift_opcode_integer(interpreter
, io
);
501 =item C<tatic FLOATVAL
502 shift_opcode_number(Parrot_Interp interpreter, IMAGE_IO *io)>
504 Removes and returns an number from the start of the C<*io> "stream".
511 shift_opcode_number(Parrot_Interp interpreter
, IMAGE_IO
*io
)
513 const char * const start
= (char*)io
->image
->strstart
;
515 PF_fetch_number(io
->pf
, (opcode_t
**) &io
->image
->strstart
);
517 io
->image
->bufused
-= ((char*)io
->image
->strstart
- start
);
518 assert((int)io
->image
->bufused
>= 0);
524 =item C<static STRING*
525 shift_opcode_string(Parrot_Interp interpreter, IMAGE_IO *io)>
527 Removes and returns a string from the start of the C<*io> "stream".
534 shift_opcode_string(Parrot_Interp interpreter
, IMAGE_IO
*io
)
536 char * const start
= (char*)io
->image
->strstart
;
538 PF_fetch_string(interpreter
, io
->pf
, (opcode_t
**) &io
->image
->strstart
);
540 io
->image
->bufused
-= ((char*)io
->image
->strstart
- start
);
541 assert((int)io
->image
->bufused
>= 0);
549 =head2 Helper Functions
560 pmc_add_ext(Parrot_Interp interpreter, PMC *pmc)>
562 Adds a C<PMC_EXT> to C<*pmc>.
569 pmc_add_ext(Parrot_Interp interpreter
, PMC
*pmc
)
571 if (pmc
->vtable
->flags
& VTABLE_PMC_NEEDS_EXT
)
572 add_pmc_ext(interpreter
, pmc
);
578 cleanup_next_for_GC_pool(Parrot_Interp interpreter,
579 struct Small_Object_Pool *pool)>
581 Sets all the C<next_for_GC> pointers to C<NULL>.
588 cleanup_next_for_GC_pool(Parrot_Interp interpreter
,
589 struct Small_Object_Pool
*pool
)
591 struct Small_Object_Arena
*arena
;
593 for (arena
= pool
->last_Arena
; arena
; arena
= arena
->prev
) {
594 PMC
*p
= arena
->start_objects
;
597 for (i
= 0; i
< arena
->used
; i
++) {
598 if (!PObj_on_free_list_TEST(p
)) {
600 PMC_next_for_GC(p
) = NULL
;
602 p
= (PMC
*)((char *)p
+ sizeof(PMC
));
610 cleanup_next_for_GC(Parrot_Interp interpreter)>
612 Cleans up the C<next_for_GC> pointers.
619 cleanup_next_for_GC(Parrot_Interp interpreter
)
621 cleanup_next_for_GC_pool(interpreter
,
622 interpreter
->arena_base
->pmc_pool
);
623 cleanup_next_for_GC_pool(interpreter
,
624 interpreter
->arena_base
->constant_pmc_pool
);
628 * this function setup stuff may be replaced by a real PMC
630 * TODO add read/write header functions, e.g. vtable->init_pmc
634 static image_funcs ascii_funcs
= {
645 static image_funcs opcode_funcs
= {
650 shift_opcode_integer
,
660 ft_init(Parrot_Interp interpreter, visit_info *info)>
662 Initializes the freeze/thaw subsystem.
669 ft_init(Parrot_Interp interpreter
, visit_info
*info
)
671 STRING
*s
= info
->image
;
674 info
->image_io
= mem_sys_allocate(sizeof(IMAGE_IO
));
675 info
->image_io
->image
= s
= info
->image
;
677 info
->image_io
->vtable
= &ascii_funcs
;
679 info
->image_io
->vtable
= &opcode_funcs
;
681 pf
= info
->image_io
->pf
= PackFile_new(interpreter
, 0);
682 if (info
->what
== VISIT_FREEZE_NORMAL
||
683 info
->what
== VISIT_FREEZE_AT_DESTRUCT
) {
685 op_check_size(interpreter
, s
, PACKFILE_HEADER_BYTES
);
686 mem_sys_memcopy(s
->strstart
, pf
->header
, PACKFILE_HEADER_BYTES
);
687 s
->bufused
+= PACKFILE_HEADER_BYTES
;
688 s
->strlen
+= PACKFILE_HEADER_BYTES
;
691 if (string_length(interpreter
, s
) < PACKFILE_HEADER_BYTES
) {
692 real_exception(interpreter
, NULL
, E_IOError
,
693 "bad string to thaw");
695 mem_sys_memcopy(pf
->header
, s
->strstart
, PACKFILE_HEADER_BYTES
);
696 PackFile_assign_transforms(pf
);
697 s
->bufused
-= PACKFILE_HEADER_BYTES
;
698 LVALUE_CAST(char *, s
->strstart
) += 16;
701 info
->last_type
= -1;
702 info
->id_list
= pmc_new(interpreter
, enum_class_Array
);
704 info
->extra_flags
= EXTRA_IS_NULL
;
705 info
->container
= NULL
;
708 static void visit_todo_list(Parrot_Interp
, PMC
*, visit_info
* info
);
709 static void add_pmc_todo_list(Parrot_Interp
, PMC
*, visit_info
* info
);
714 todo_list_init(Parrot_Interp interpreter, visit_info *info)>
716 Initializes the C<*info> lists.
723 todo_list_init(Parrot_Interp interpreter
, visit_info
*info
)
725 info
->visit_pmc_now
= visit_todo_list
;
726 info
->visit_pmc_later
= add_pmc_todo_list
;
727 /* we must use PMCs here, so that they get marked properly */
728 info
->todo
= pmc_new(interpreter
, enum_class_Array
);
729 info
->seen
= Parrot_new_INTVAL_hash(interpreter
, 0);
731 ft_init(interpreter
, info
);
736 =item C<PARROT_INLINE static void
737 freeze_pmc(Parrot_Interp interpreter, PMC *pmc, visit_info *info,
738 int seen, UINTVAL id)>
744 PARROT_INLINE
static void
745 freeze_pmc(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
,
746 int seen
, UINTVAL id
)
748 IMAGE_IO
* const io
= info
->image_io
;
751 if (PMC_IS_NULL(pmc
)) {
752 /* NULL + seen bit */
753 io
->vtable
->push_pmc(interpreter
, io
, (PMC
*) 1);
756 type
= pmc
->vtable
->base_type
;
758 if (PObj_is_object_TEST(pmc
))
759 type
= enum_class_ParrotObject
;
761 if (info
->extra_flags
) {
763 io
->vtable
->push_pmc(interpreter
, io
, (PMC
*)id
);
764 io
->vtable
->push_integer(interpreter
, io
, info
->extra_flags
);
767 id
|= 1; /* mark bit 0 if this PMC is known */
769 else if (type
== info
->last_type
) {
770 id
|= 2; /* mark bit 1 and don't write type */
772 io
->vtable
->push_pmc(interpreter
, io
, (PMC
*)id
);
773 if (! (id
& 3)) { /* else write type */
774 io
->vtable
->push_integer(interpreter
, io
, type
);
775 info
->last_type
= type
;
781 =item C<PARROT_INLINE static int
782 thaw_pmc(Parrot_Interp interpreter, visit_info *info,
783 UINTVAL *id, INTVAL *type)>
785 Freeze and thaw a PMC (id).
787 For example, the ASCII representation of the C<Array>
789 P0 = [P1=666, P2=777, P0]
793 0xdf4 30 3 0xdf8 33 666 0xdf2 777 0xdf5
795 where 30 is C<class_enum_Array>, 33 is C<class_enum_Integer>, the
796 type of the second C<Integer> is suppressed, the repeated P0 has bit 0
803 PARROT_INLINE
static int
804 thaw_pmc(Parrot_Interp interpreter
, visit_info
*info
,
805 UINTVAL
*id
, INTVAL
*type
)
808 IMAGE_IO
*io
= info
->image_io
;
811 info
->extra_flags
= EXTRA_IS_NULL
;
812 n
= io
->vtable
->shift_pmc(interpreter
, io
);
813 if ( ((UINTVAL
) n
& 3) == 3) {
814 /* pmc has extra data */
815 info
->extra_flags
= io
->vtable
->shift_integer(interpreter
, io
);
817 else if ( (UINTVAL
) n
& 1) { /* seen PMCs have bit 0 set */
820 else if ( (UINTVAL
) n
& 2) { /* prev PMC was same type */
821 *type
= info
->last_type
;
823 else { /* type follows */
824 info
->last_type
= *type
= io
->vtable
->shift_integer(interpreter
, io
);
826 internal_exception(1, "Unknown PMC type to thaw %d", (int) *type
);
827 if (*type
>= interpreter
->n_vtable_max
||
828 !interpreter
->vtables
[*type
]) {
829 /* that ought to be a class */
830 *type
= enum_class_ParrotClass
;
839 =item C<PARROT_INLINE static void
840 do_action(Parrot_Interp interpreter, PMC *pmc, visit_info *info,
841 int seen, UINTVAL id)>
843 Called from C<visit_next_for_GC()> and C<visit_todo_list()> to perform
844 the action specified in C<< info->what >>.
846 Currently only C<VISIT_FREEZE_NORMAL> is implemented.
852 PARROT_INLINE
static void
853 do_action(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
,
854 int seen
, UINTVAL id
)
856 switch (info
->what
) {
857 case VISIT_FREEZE_AT_DESTRUCT
:
858 case VISIT_FREEZE_NORMAL
:
859 freeze_pmc(interpreter
, pmc
, info
, seen
, id
);
861 info
->visit_action
= pmc
->vtable
->freeze
;
864 internal_exception(1, "Illegal action %d", info
->what
);
871 =item C<PARROT_INLINE static PMC*
872 thaw_create_pmc(Parrot_Interp interpreter, PMC *pmc, visit_info *info,
875 Called from C<do_thaw()> to attach the vtable etc. to C<*pmc>.
881 PARROT_INLINE
static PMC
*
882 thaw_create_pmc(Parrot_Interp interpreter
, const visit_info
*info
,
886 switch (info
->what
) {
887 case VISIT_THAW_NORMAL
:
888 pmc
= pmc_new_noinit(interpreter
, type
);
890 case VISIT_THAW_CONSTANTS
:
891 pmc
= constant_pmc_new_noinit(interpreter
, type
);
895 internal_exception(1, "Illegal visit_next type");
903 =item C<PARROT_INLINE static void
904 do_thaw(Parrot_Interp interpreter, PMC* pmc, visit_info *info)>
906 Called by C<visit_todo_list_thaw()> to thaw and return a PMC.
908 C<seen> is false if this is the first time the PMC has been encountered.
914 PARROT_INLINE
static void
915 do_thaw(Parrot_Interp interpreter
, PMC
* pmc
, visit_info
*info
)
921 type
= 0; /* it's set below, avoid compiler warning. */
922 must_have_seen
= thaw_pmc(interpreter
, info
, &id
, &type
);
929 if (!info
->thaw_result
)
930 info
->thaw_result
= pmc
;
932 *info
->thaw_ptr
= pmc
;
936 pos
= list_get(interpreter
, PMC_data(info
->id_list
), id
, enum_type_PMC
);
937 if (pos
== (void*)-1)
945 if (info
->extra_flags
== EXTRA_IS_PROP_HASH
) {
946 Parrot_default_thaw(interpreter
, pmc
, info
);
949 /* else maybe VTABLE_thaw ... but there is no other extra stuff */
951 #if FREEZE_USE_NEXT_FOR_GC
953 * the next_for_GC method doesn't keep track of repeated scalars
954 * and such, as these are lacking the next_for_GC pointer, so
955 * these are just duplicated with their data.
956 * But we track these when thawing, so that we don't create dups
958 if (!must_have_seen
) {
959 /* so we must consume the bytecode */
960 VTABLE_thaw(interpreter
, pmc
, info
);
963 assert(must_have_seen
);
968 DOD_WRITE_BARRIER(interpreter, info->container, NULL, pmc);
970 *info
->thaw_ptr
= pmc
;
974 assert(!must_have_seen
);
975 pmc
= thaw_create_pmc(interpreter
, info
, type
);
977 VTABLE_thaw(interpreter
, pmc
, info
);
978 if (info
->extra_flags
== EXTRA_CLASS_EXISTS
) {
981 info
->extra_flags
= 0;
983 if (!info
->thaw_result
)
984 info
->thaw_result
= pmc
;
986 if (info
->container
) {
987 DOD_WRITE_BARRIER(interpreter
, info
->container
, NULL
, pmc
);
989 *info
->thaw_ptr
= pmc
;
991 list_assign(interpreter
, PMC_data(info
->id_list
), id
, pmc
, enum_type_PMC
);
992 /* remember nested aggregates depth first */
994 list_unshift(interpreter
, PMC_data(info
->todo
), pmc
, enum_type_PMC
);
999 id_from_pmc(Parrot_Interp interpreter
, PMC
* pmc
)
1001 UINTVAL id
= 1; /* first PMC in first arena */
1002 struct Small_Object_Arena
*arena
;
1003 struct Small_Object_Pool
*pool
;
1006 pmc
= (PMC
*)PObj_to_ARENA(pmc
);
1007 pool
= interpreter
->arena_base
->pmc_pool
;
1008 for (arena
= pool
->last_Arena
; arena
; arena
= arena
->prev
) {
1009 ptr_diff
= (ptrdiff_t)pmc
- (ptrdiff_t)arena
->start_objects
;
1010 if (ptr_diff
>= 0 && ptr_diff
<
1011 (ptrdiff_t)(arena
->used
* pool
->object_size
)) {
1012 assert(ptr_diff
% pool
->object_size
== 0);
1013 id
+= ptr_diff
/ pool
->object_size
;
1016 id
+= arena
->total_objects
;
1018 pool
= interpreter
->arena_base
->constant_pmc_pool
;
1019 for (arena
= pool
->last_Arena
; arena
; arena
= arena
->prev
) {
1020 ptr_diff
= (ptrdiff_t)pmc
- (ptrdiff_t)arena
->start_objects
;
1021 if (ptr_diff
>= 0 && ptr_diff
<
1022 (ptrdiff_t)(arena
->used
* pool
->object_size
)) {
1023 assert(ptr_diff
% pool
->object_size
== 0);
1024 id
+= ptr_diff
/ pool
->object_size
;
1027 id
+= arena
->total_objects
;
1030 internal_exception(1, "Couldn't find PMC in arenas");
1037 add_pmc_next_for_GC(Parrot_Interp interpreter, PMC *pmc, visit_info *info)>
1039 Remembers the PMC for later processing.
1046 add_pmc_next_for_GC(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
)
1049 PMC_next_for_GC(info
->mark_ptr
) = pmc
;
1050 info
->mark_ptr
= PMC_next_for_GC(pmc
) = pmc
;
1056 =item C<PARROT_INLINE static int
1057 next_for_GC_seen(Parrot_Interp interpreter, PMC *pmc, visit_info *info,
1060 Remembers next child to visit via the C<next_for_GC pointer> generate a
1061 unique ID per PMC and freeze the ID (not the PMC address) so thaw the
1062 hash-lookup can be replaced by an array lookup then which is a lot
1069 PARROT_INLINE
static int
1070 next_for_GC_seen(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
,
1074 if (PMC_IS_NULL(pmc
)) {
1080 * we can only remember PMCs with a next_for_GC pointer
1081 * which is located in pmc_ext
1085 if (PMC_next_for_GC(pmc
)) {
1089 /* put pmc at the end of the list */
1090 PMC_next_for_GC(info
->mark_ptr
) = pmc
;
1091 /* make end self-referential */
1092 info
->mark_ptr
= PMC_next_for_GC(pmc
) = pmc
;
1095 *id
= id_from_pmc(interpreter
, pmc
);
1102 add_pmc_todo_list(Parrot_Interp interpreter, PMC *pmc, visit_info *info)>
1104 Remembers the PMC to be processed later.
1111 add_pmc_todo_list(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
)
1113 list_push(interpreter
, PMC_data(info
->todo
), pmc
, enum_type_PMC
);
1118 =item C<PARROT_INLINE static int
1119 todo_list_seen(Parrot_Interp interpreter, PMC *pmc, visit_info *info,
1122 Returns true if the PMC was seen, otherwise it put it on the todo list,
1123 generates an ID (tag) for PMC, offset by 4 as are addresses, low bits
1130 PARROT_INLINE
static int
1131 todo_list_seen(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
,
1134 HashBucket
* const b
=
1135 parrot_hash_get_bucket(interpreter
, PMC_struct_val(info
->seen
), pmc
);
1138 *id
= (UINTVAL
) b
->value
;
1142 info
->id
+= 4; /* next id to freeze */
1144 parrot_hash_put(interpreter
, PMC_struct_val(info
->seen
), pmc
, (void*)*id
);
1145 /* remember containers */
1147 list_unshift(interpreter
, PMC_data(info
->todo
), pmc
, enum_type_PMC
);
1154 visit_next_for_GC(Parrot_Interp interpreter, PMC* pmc, visit_info* info)>
1156 C<visit_child> callbacks:
1158 Checks if the PMC was seen, generate an ID for it if not, then do the
1166 visit_next_for_GC(Parrot_Interp interpreter
, PMC
* pmc
, visit_info
* info
)
1169 const int seen
= next_for_GC_seen(interpreter
, pmc
, info
, &id
);
1171 internal_exception(1, "todo convert to depth first");
1172 do_action(interpreter
, pmc
, info
, seen
, id
);
1174 * TODO probe for class methods that override the default.
1175 * To avoid overhead, we could have an array[class_enums]
1176 * which (after first find_method) has a bit, if a user
1177 * callback is there.
1180 (info
->visit_action
)(interpreter
, pmc
, info
);
1186 visit_todo_list(Parrot_Interp interpreter, PMC* pmc, visit_info* info)>
1188 Checks the seen PMC via the todo list.
1195 visit_todo_list(Parrot_Interp interpreter
, PMC
* pmc
, visit_info
* info
)
1200 if (PMC_IS_NULL(pmc
)) {
1205 seen
= todo_list_seen(interpreter
, pmc
, info
, &id
);
1206 do_action(interpreter
, pmc
, info
, seen
, id
);
1208 (info
->visit_action
)(interpreter
, pmc
, info
);
1214 visit_todo_list_thaw(Parrot_Interp interpreter, PMC* old, visit_info* info)>
1216 Callback for thaw - action first.
1218 Todo-list and seen handling is all in C<do_thaw()>.
1225 visit_todo_list_thaw(Parrot_Interp interpreter
, PMC
* old
, visit_info
* info
)
1227 do_thaw(interpreter
, old
, info
);
1233 visit_loop_next_for_GC(Parrot_Interp interpreter, PMC *current,
1236 Put first item on todo list, then run as long as there are items to be
1244 visit_loop_next_for_GC(Parrot_Interp interpreter
, PMC
*current
,
1247 visit_next_for_GC(interpreter
, current
, info
);
1248 if (current
->pmc_ext
) {
1251 while (current
!= prev
) {
1252 VTABLE_visit(interpreter
, current
, info
);
1254 current
= PMC_next_for_GC(current
);
1262 visit_loop_todo_list(Parrot_Interp interpreter, PMC *current,
1271 /* XXX This should be in a header file. */
1273 Parrot_default_thawfinish(Interp
* interpreter
, PMC
* pmc
, visit_info
*info
);
1276 visit_loop_todo_list(Parrot_Interp interpreter
, PMC
*current
,
1279 List
*todo
= PMC_data(info
->todo
);
1280 PMC
*finish_list_pmc
;
1282 List
*finish_list
= NULL
; /* gcc -O3 warning */
1283 int finished_first
= 0;
1285 const int thawing
= info
->what
== VISIT_THAW_CONSTANTS
||
1286 info
->what
== VISIT_THAW_NORMAL
;
1289 * create a list that contains PMCs that need thawfinish
1291 finish_list_pmc
= pmc_new(interpreter
, enum_class_Array
);
1292 finish_list
= PMC_data(finish_list_pmc
);
1295 (info
->visit_pmc_now
)(interpreter
, current
, info
);
1297 * can't cache upper limit, visit may append items
1300 for (; (int)list_length(interpreter
, todo
); ) {
1301 current
= *(PMC
**)list_shift(interpreter
, todo
, enum_type_PMC
);
1302 VTABLE_visit(interpreter
, current
, info
);
1304 if (current
== info
->thaw_result
)
1306 if (current
->vtable
&& current
->vtable
->thawfinish
!=
1307 Parrot_default_thawfinish
)
1308 list_unshift(interpreter
, finish_list
, current
, enum_type_PMC
);
1314 * if image isn't consumed, there are some extra data to thaw
1316 if (info
->image
->bufused
> 0) {
1317 (info
->visit_pmc_now
)(interpreter
, NULL
, info
);
1321 * on thawing call thawfinish for each processed PMC
1323 if (!finished_first
) {
1325 * the first create PMC might not be in the list,
1326 * if it has no pmc_ext
1328 list_unshift(interpreter
, finish_list
,
1329 info
->thaw_result
, enum_type_PMC
);
1331 n
= (int)list_length(interpreter
, finish_list
);
1332 for (i
= 0; i
< n
; ++i
) {
1333 current
= *(PMC
**)list_get(interpreter
, finish_list
, i
,
1335 if (!PMC_IS_NULL(current
))
1336 VTABLE_thawfinish(interpreter
, current
, info
);
1344 create_image(Parrot_Interp interpreter, PMC *pmc, visit_info *info)>
1346 Allocate image to some estimated size.
1353 create_image(Parrot_Interp interpreter
, PMC
*pmc
, visit_info
*info
)
1356 if (!PMC_IS_NULL(pmc
) && (VTABLE_does(interpreter
, pmc
,
1357 string_from_cstring(interpreter
, "array", 0)) ||
1358 VTABLE_does(interpreter
, pmc
,
1359 string_from_cstring(interpreter
, "hash", 0)))) {
1360 const INTVAL items
= VTABLE_elements(interpreter
, pmc
);
1362 * TODO check e.g. first item of aggregate and estimate size
1364 len
= items
* FREEZE_BYTES_PER_ITEM
;
1367 len
= FREEZE_BYTES_PER_ITEM
;
1369 info
->image
= string_make_empty(interpreter
, enum_stringrep_one
, len
);
1375 run_thaw(Parrot_Interp interpreter, STRING* image, visit_enum_type what)>
1377 Performs thawing. C<what> indicates what to be thawed.
1379 Thaw could use the C<next_for_GC> pointers as todo-list too, but this
1380 would need 2 runs through the arenas to clean the C<next_for_GC>
1383 For now it seems cheaper to use a list for remembering contained
1384 aggregates. We could of course decide dynamically, which strategy to
1385 use, e.g.: given a big image, the first thawed item is a small
1386 aggregate. This implies, it probably contains (or some big strings) more
1387 nested containers, for which the C<next_for_GC> approach could be a win.
1394 run_thaw(Parrot_Interp interpreter
, STRING
* image
, visit_enum_type what
)
1398 const UINTVAL bufused
= image
->bufused
;
1402 * if we are thawing a lot of PMCs, its cheaper to do
1403 * a DOD run first and then block DOD - the limit should be
1404 * chosen so that no more then one DOD run would be triggered
1408 * md5_3.pir shows a segfault during thawing the config hash
1409 * info->thaw_ptr becomes invalid - seems that the hash got
1410 * collected under us.
1412 if (1 || (string_length(interpreter
, image
) > THAW_BLOCK_DOD_SIZE
)) {
1413 Parrot_do_dod_run(interpreter
, 1);
1414 Parrot_block_DOD(interpreter
);
1415 Parrot_block_GC(interpreter
);
1419 info
.what
= what
; /* _NORMAL or _CONSTANTS */
1420 todo_list_init(interpreter
, &info
);
1421 info
.visit_pmc_now
= visit_todo_list_thaw
;
1422 info
.visit_pmc_later
= add_pmc_todo_list
;
1424 info
.thaw_result
= NULL
;
1428 visit_loop_todo_list(interpreter
, NULL
, &info
);
1430 * thaw does "consume" the image string by incrementing strstart
1431 * and decrementing bufused - restore that
1433 LVALUE_CAST(char *, image
->strstart
) -= bufused
;
1434 image
->bufused
= bufused
;
1435 assert(image
->strstart
>= PObj_bufstart(image
));
1438 Parrot_unblock_DOD(interpreter
);
1439 Parrot_unblock_GC(interpreter
);
1441 PackFile_destroy(interpreter
, info
.image_io
->pf
);
1442 mem_sys_free(info
.image_io
);
1443 return info
.thaw_result
;
1450 =head2 Public Interface
1455 Parrot_freeze_at_destruct(Parrot_Interp interpreter, PMC* pmc)>
1457 This function must not consume any resources (except the image itself).
1458 It uses the C<next_for_GC> pointer, so its not reentrant and must not be
1459 interrupted by a DOD run.
1466 Parrot_freeze_at_destruct(Parrot_Interp interpreter
, PMC
* pmc
)
1470 Parrot_block_DOD(interpreter
);
1471 cleanup_next_for_GC(interpreter
);
1472 info
.what
= VISIT_FREEZE_AT_DESTRUCT
;
1473 info
.mark_ptr
= pmc
;
1474 info
.thaw_ptr
= NULL
;
1475 info
.visit_pmc_now
= visit_next_for_GC
;
1476 info
.visit_pmc_later
= add_pmc_next_for_GC
;
1477 create_image(interpreter
, pmc
, &info
);
1478 ft_init(interpreter
, &info
);
1480 visit_loop_next_for_GC(interpreter
, pmc
, &info
);
1482 Parrot_unblock_DOD(interpreter
);
1483 PackFile_destroy(interpreter
, info
.image_io
->pf
);
1484 mem_sys_free(info
.image_io
);
1491 Parrot_freeze(Parrot_Interp interpreter, PMC* pmc)>
1493 Freeze using either method.
1500 Parrot_freeze(Parrot_Interp interpreter
, PMC
* pmc
)
1502 #if FREEZE_USE_NEXT_FOR_GC
1504 * we could do a DOD run here before, to free resources
1506 return Parrot_freeze_at_destruct(interpreter
, pmc
);
1509 * freeze using a todo list and seen hash
1510 * Please note that both have to be PMCs, so that trace_system_stack
1511 * can call mark on the PMCs
1515 info
.what
= VISIT_FREEZE_NORMAL
;
1516 create_image(interpreter
, pmc
, &info
);
1517 todo_list_init(interpreter
, &info
);
1519 visit_loop_todo_list(interpreter
, pmc
, &info
);
1521 PackFile_destroy(interpreter
, info
.image_io
->pf
);
1522 mem_sys_free(info
.image_io
);
1530 Parrot_thaw(Parrot_Interp interpreter, STRING* image)>
1532 Thaw a PMC, called from the C<thaw> opcode.
1539 Parrot_thaw(Parrot_Interp interpreter
, STRING
* image
)
1541 return run_thaw(interpreter
, image
, VISIT_THAW_NORMAL
);
1547 Parrot_thaw_constants(Parrot_Interp interpreter, STRING* image)>
1549 Thaw the constants. This is used by PackFile for unpacking PMC
1557 Parrot_thaw_constants(Parrot_Interp interpreter
, STRING
* image
)
1559 return run_thaw(interpreter
, image
, VISIT_THAW_CONSTANTS
);
1565 Parrot_clone(Parrot_Interp interpreter, PMC* pmc)>
1567 There are for sure shortcuts to clone faster, e.g. always thaw the image
1568 immediately or use a special callback. But for now we just thaw a frozen
1576 Parrot_clone(Parrot_Interp interpreter
, PMC
* pmc
)
1578 return Parrot_thaw(interpreter
, Parrot_freeze(interpreter
, pmc
));
1587 The seen-hash version for freezing might go away sometimes.
1591 Lot of discussion on p6i and F<docs/dev/pmc_freeze.pod>.
1595 Initial version by leo 2003.11.03 - 2003.11.07.
1604 * c-file-style: "parrot"
1606 * vim: expandtab shiftwidth=4: