13-Sep-2013 00:11, H. S. Teoh пишет:
On Thu, Sep 12, 2013 at 11:13:30PM +0400, Dmitry Olshansky wrote:
12-Sep-2013 20:51, H. S. Teoh пишет:
On Thu, Sep 12, 2013 at 07:50:25PM +0400, Dmitry Olshansky wrote:
[...]
Better option is to have finalizer hooked up to set some flag. Then
_after_ restoring the pointer we consult that flag variable.
Good idea. The problem is, how to set a finalizer on a memory block
that can change in size? The OP's original situation was that the
buffer can be extended while in use, but I don't know of any D type
that can associate a dtor with a ubyte[] array (note that the GC
collecting the wrapper struct/class around the ubyte[] is not the
same as collecting the actual memory block storing the ubyte[] -- the
former can happen without the latter).
Double indirection? Allocate a class that has finalizer, hold that
via weak-ref. The wrapper in turn contains a pointer to the buffer.
The interesting point then is that one may allocate said buffer via
C's realloc.
Then once helper struct is collected the finalizer is called and
this is where we call free to cleanup C's heap.
I'm thinking this actually is going to work.
[...]
Interesting idea, use C's malloc/realloc to hold the actual buffer. Only
possible catch is, will that cause the GC to collect when it runs out of
memory (which is the whole point of the OP's question)? I.e., does it
make a difference in GC behaviour to allocate, say, 10MB from the GC vs.
allocating 10MB from malloc/realloc?
The only problem I can foresee is that when it runs the collection
(*and* being tight on RAM) the C heap will not return said chunk back to
OS. Then GC won't pick up that memory, and we'd get out of ram.
I would safely assume however that for big buffers a mmap/munmap is
called (or its analogue) and hence memory is returned back to OS. That's
what all allocators do for huge chunks by anyway.
Otherwise we are still in a good shape, the memory will eventually be
freed, yet we get to reuse it quite cheaply in a tight loop. I don't
expect collections to run in these all that often ;)
Assuming we have that settled, something like this should work:
bool isValid;
final class BufWrapper {
void* ptrToMallocedBuf;
this(void* ptr) {
// We need this, 'cos otherwise we don't know if
// our weak ref to BufWrapper is still valid!
isValid = true;
ptrToMallocedBuf = ptr;
}
~this() {
// If we're being collected, free the real
// buffer too.
free(ptrToMallocedBuf);
isValid = false;
}
}
// WeakPointer masks the pointer to BufWrapper in some suitable
// way so that the GC will collect it when needed.
WeakPointer!BufWrapper wrappedBufRef;
void doWork(...) {
void* buf;
Careful here - you really have first to get a pointer ... THEN check if
it's valid.
if (!isValid) {
buf = realloc(null, bufSize);
wrappedBufRef.set(buf);
} else {
//otherwise at this point GC.collect runs and presto, memory is freed
//too bad such a thing will never show up in unittests
buf = wrappedBufRef.get();
}
// use buf here.
}
Checking the flag should be somehow part of weak ref job.
I'd rather make it less error prone:
void* buf;
//unmask pointer, do the flag check - false means was freed
if(!weakRef.readTo(buf)){
//create & set new buf
buf = realloc(...);
}
... //use buf
weakRef.set(buf);
I think I'd code it up if nobody beats me to it as I need the same exact
pattern for std.regex anyway.
--
Dmitry Olshansky