On 9/10/2011 5:07 PM, Andrei Alexandrescu wrote:
On 9/6/11 2:53 PM, Jonathan M Davis wrote:
Okay. Onto the next item in the review queue. I'm going to be the review
manager this time around, and we're going to be reviewing dsimcha's
region
allocator. The review starts now and will end on 2011-09-21 at
midnight in UTC
(so about 2 weeks from now, when the 21st begins). Assuming that we're
ready
to vote at that point, I'll start a thread for voting for inclusion in
Phobos,
and the vote will last a week. If it's not ready to be voted on for some
reason (such as dsimcha needing to go back and make a bunch of changes
that
are going to need to be reviewed), then voting will be postponed and
it'll be
put back in the review queue once it's ready for review again.
Docs:
http://cis.jhu.edu/~dsimcha/d/phobos/std_regionallocator.html
Unfortunately my review is incomplete because I don't see the allocator
interface.
In theory it should be possible to first define the region allocator and
then the allocator interface. The other way around is also valid.
However, clearly it's best to define the general allocator interface
_together_ with the region allocator.
I mostly took your proposal from a few months ago and doctored it
slightly. Basically, my theory was to define the allocator interface by
example (both RegionAllocator and GCAllocator being examples) and
resolve any ambiguities through the review process before creating more
formal documentation of the allocator interface. I'll probably create a
std.allocator.allocator, which would be similar to the top of
std.container and contain documentation of the general allocator interface.
Containers will need an allocator, and I think it's best to make it a
straight dynamic interface. In that design, the region allocator would
offer the scoped structs PLUS factory methods in those structs that
return implementations for those interfaces.
"Dynamic" as in using the `interface` keyword? My concern with this is
that a major purpose of RegionAllocator is to avoid the GC and its
global lock like the plague. I think it would be a **terrible** misuse
of things if you had to perform a heap allocation to get at the dynamic
interface. I'll think about this, though. There may be an easy
workaround.
My other concern is that dynamic interfaces would break ref counting of
RegionAllocator instances because the GC frees things in an undefined
order, and I guess you'd have to rely on the GC to free a region. Maybe
I'm completely misunderstanding your suggestion, though.
* It's region_allocator or region.allocator, not regionallocator.
Ironically a good example of why is in this very name - is it "region
allocator" or "regional locator"?
You mean for the module name? I was thinking std.allocators.region.
Are you ok with this, too?
* Use "bumped up" and "bumped down" or something instead of
"incremented" and "decremented", which imply 1 as the unit.
Good point.
* Enumerated paragraphs are not visually distinguished.
If you mean the lists of advantages/disadvantages at the top of the
module, I'd like to distinguish them better but don't know how to in
DDoc. Any advice?
* Synopsis should go before the textual intro.
Ok.
* Exception should be at the bottom, not the top of the dox.
Makes sense. I'm also thinking it should be derived from Error, not
Exception and be renamed RegionAllocatorError, since it's only thrown on
API misuse, i.e. bugs in client code. I could also use asserts, but I
decided not to because I've made it so that everything can be checked
cheaply and measured to make sure the checking doesn't lead to
performance problems, and because certain misuses of the API would be
virtually impossible to debug without the exception error messages.
* There's a problem with "std.regionallocator
RegionAllocator.regionallocator RegionAllocator" - i.e. a macro that
didn't expand to what it should have.
I noticed this, too, but have no idea how to fix it.
* I didn't understand from the dox what the relationship between
RegionAllocator and RegionAllocatorStack is, mainly because
RegionAllocator is used before having been defined. (This is a difficult
problem in general.)
Hmm, maybe I should move RegionAllocator before RegionAllocatorStack and
focus on the thread-local default instantiation first, since this is
what people should use for most simple use cases. Creating explicit
RegionAllocatorStack instances is a more advanced use case.
Anyhow, the explanation FYI is that a RegionAllocatorStack is a large
chunk of memory and can be allocated from by the RegionAllocator that's
conceptually at the top of the stack. When the last instance of the
RegionAllocator at the top of the stack goes out of scope (this is kept
track of via reference counting), the RegionAllocatorStack is freed up
to the memory used by the next RegionAllocator down on the stack.
Conceptually, you can think of this as a "stack of simple regions" where
"simple regions" are the canonical ones described in the literature
rather than the somewhat embellished ones in this module.
* The GCScan enum is defined uncomfortably far from the places where
it's relevant. (Another difficult matter.)
I agree, but I don't really see any easy way to fix this. I could put
GCScan inside RegionAllocatorStack, but who wants to type
RegionAllocatorStack.GCScan.yes just to get a simple flag?
* I think "scanThreadLocalStack" should be "threadLocalStackIsScannable"
so as people don't confuse it with an action.
Sounds good.
* newArray could only take the element type as a template parameter; the
number of dimensions in unequivocally determined from the number of
arguments.
Good point. Actually, this can probably be changed in
std.array.uninitializedArray, too. There was some reason why I did it
the way I did, but I don't remember why. I'll look into it.
* In fact new(Uninitialized)Array should not be a member as its workings
are not specific to this allocator. It should be a free function taking
an allocator as a parameter.
My concern about this is that some allocators might want to do some
"special" things based on knowing the type. A good example is that
GCAllocator decides whether to scan for pointers based on knowing the
type. RegionAllocator has a completely different method for determining
this policy. When/if I make a std.allocators.allocator (which would
contain documentation of the general allocator interface, etc.) I can
make a mixin template that provides default implementations of things
that have obvious implementations in terms of lower-level allocator
features. (I think array() would also be included here.) This could be
mixed in and overridden if need be, but would provide the defaults to
save on code duplication across allocators.
* The documentation for resize() is the first time "allocator interface"
is mentioned (without having been defined). Wait a minute. I was
hungrily looking for this since I opened the doc.
Yeah, see above about documentation of the allocator interface.
* I think alignBytes should be a compile-time computable property, and
that the documentation should mention that.
Good point.
* isAutomatic, isScoped, freeIsChecked should also be static and
statically computable properties (perhaps not freeIsChecked).
I think they are (actually, I think they're enums) and DDoc just doesn't
reflect this. If not, then I'll definitely fix it.
Andrei