Bennie is asking how frequently interior pointers are likely to be used.
It's a good question. It's hard to answer, because we don't actually have
the feature available, so nobody can code with it. What we *can* say is
that it's a pattern that is used a lot in systems codes.


I wouldn't say it's common, exactly, but there are a lot of patterns in
systems codes that go "well, I know that this thing is a field of that
thing, and it *only* lives in that thing, and at a known offset".  Use
cases for this arise from legacy data structures, but more commonly from
linked lists, queues (which are really linked lists), and custom allocators.


*Embedded Links*

The example from the Coyotos kernel would be in the
ObjectHeader<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/ObjectHeader.h>structure.
It is the first element of the
MemHeader<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/ObjectHeader.h#l149>and
Process<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/Process.h>structures.
MemHeader is used in bare form as the metadata structure for
pages. It is also the leading substructure of the
GPT<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/GPT.h>data
structure. The first member of ObjectHeader is
Ageable<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/AgeList.h>,
which in turn has a
Link<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/Link.h>as
it's first member, by which every object ends up on exactly one age
list
at all times.

If an object needs to live on just *one* linked list, you might imagine
that you could move it to the front of the object, use single inheritance,
and have done. That's what we did with the Ageable structure in Coyotos.
The age lists in Coyotos are partitioned by type: Process structures are on
a different age queue than GPT structures. That lets us link the objects as
if they were of type Link, but remove them as type Process (or whatever).
Which gives us shared code but weak typing. We could hypothetically
preserve that shared code by introducing a new kind of pointer type (I'm
making up a syntax):

P.age


Meaning "for some reference type P, the type of an interior pointer to the
age field of P". So this is a *statically typed* interior pointer. Those
are a very minor nuisance for GC, but they are *only* a minor nuisance as
long as they are statically typed. , Note that this type is a subtype of
Ageable. In fact, we could extend this in the obvious way to:

P.age.link


and arrive at a reference that is of union type (P | typeof(P.age) <
Ageable | typeof(P.age.link) < Link) and apply the appropriate offsets
during cast operations. I'm actually not sure if we want to think of this
as a union type or as a new and different sort of thing (containment
typing?).

So if we could express this type, we could say "well, I want a linked list
running through the age field of the process structure, so I'll make the
link structure parametric over subtypes of Link and then instantiate one
over this P.age type". And we could do the same thing for the
queue_link<http://dev.eros-os.com/hg/coyotos/trunk/file/tip/src/sys/kerninc/Process.h#l234>field
of the Process structure. The two link structures can't both live at
the front of the object simultaneously, but with static typing that doesn't
matter.

Not so fast. In both cases, the linked lists are circular. Which is fine,
except that the link element at the head of the age list isn't stored in a
Process, so the linked list elements actually *are* heterogeneously typed -
because the head element is an exceptional case. It's important, in this
data structure, that the head element is unique, because that's what lets
us get away with treating it exceptionally. And I don't know how to
statically type that.

In these two cases, however, we can redesign the data structure. The
objects could be linked circularly, and the link head could point at the
first member of the list. This works because given the object, we know how
to find the list. If we do that, then the entire typing problem goes away.
In fact, the only reason containment typing is of interest at that point is
to avoid some constant offset computations at run time. Well, except for
the fact that they might by us some implementation-level code reuse.


So how useful is this to general programs? I really don't know, though I
think it might reduce the storage required for certain kinds of
collections. It's a common idiom in graphics toolkits. How important is it
in things like kernels and systems programs? Really important. Read on to
get a sense of how constrained we are for space in a kernel. In brief, *every
word counts*.


*Custom Allocators*

Bennie asked whether building a custom allocator from an array was
important. The answer is "yes and no". First, let me give a sense of just
how tight for space we are in a kernel to give you a sense of the problem.

In Coyotos, we use a bare MemHeader structure for every real page frame. On
x86, the Coyotos kernel gets 0.5G of virtual space (total), but the machine
can have up to 64G of real physical memory. Pages are 4KB, so if the
MemHeader structure is 64 bytes, and we need one per frame, that occupies
(2^36 / 2^12) * 2^6 = 2^30 = 1G of memory. Our actual structure is around
48 bytes, which lets us support up to 16G of physical ram in that kernel
configuration. And looking at the data structure I see that we have
something out of position and I should move a field.

Right now, MemHeader structures are allocated from an array. If we switched
to an array of* references* to MemHeaders, we would effectively add a word
to the cost of every MemHeader. We simply don't *have* an additional word
for these structures.

What we *could *do, I suppose, is move the array notion into the allocator
and deal with it there as an unsafe operation. In a kernel that would be
okay, because it's very well localized. We need some unsafe operations down
there in any case, because that's the layer where we are converting raw
memory to object storage.

The thing to note here, I suppose, is that that containment typing trick
doesn't work in this case. We aren't dealing with a particular, known
element.


shap
_______________________________________________
bitc-dev mailing list
bitc-dev@coyotos.org
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to