Inheritance: IDisposable
        public NewUnusedSegment allocateNewSegment(LayerWriteGroup tx, int length)
        {
            // use one big nasty lock to prevent race conditions
            lock (this) {

                // try to find an extent with enough space to carve off a chunk
                foreach (var rec in store.scanForward(new ScanRange<RecordKey>(freelist_prefix,
                                RecordKey.AfterPrefix(freelist_prefix), null))) {
                    FreespaceExtent extent = FreespaceExtent.unpack(rec.Value.data);

                    if (extent.length() == length) {
                        // the extent is exactly the right size... make it pending
                        LayerWriteGroup makepending_wg =
                            tx.mylayer.newWriteGroup(type: LayerWriteGroup.WriteGroupType.DISK_ATOMIC_NOFLUSH);

                        // add a pending entry for this block
                        {
                            RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/PENDING");
                            key.appendKeyPart(new RecordKeyType_Long(extent.start_addr));
                            makepending_wg.setValue(key, RecordUpdate.WithPayload(extent.pack()));
                        }

                        // remove the freelist entry
                        {
                            RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/EXTENTS");
                            key.appendKeyPart(new RecordKeyType_Long(extent.end_addr));
                            makepending_wg.setValue(key, RecordUpdate.DeletionTombstone());
                        }

                        makepending_wg.finish();
                        return new NewUnusedSegment(store, extent);
                    } else if (extent.length() > length) {

                        // TODO: carve a piece off the extent and return the pending piece

                    }
                }

                // if we can't find a free segment, grow the heap
                return growHeap(tx, length);

                // TODO: then carve a segment out of the new grown heap

            }
        }
        public void mapSegment(LayerWriteGroup tx, int use_gen, 
            RecordKey start_key, RecordKey end_key, IRegion reader)
        {
            if (! (tx.type == LayerWriteGroup.WriteGroupType.DISK_ATOMIC_NOFLUSH ||
                   tx.type == LayerWriteGroup.WriteGroupType.DISK_ATOMIC_FLUSH)) {
                       throw new Exception("NewUnusedSegment.mapSegment() must be provided an ATOMIC write group");
            }

            // remove the pending entry
            RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/PENDING");
            key.appendKeyPart(new RecordKeyType_Long(reader.getStartAddress()));
            tx.setValue(key, RecordUpdate.DeletionTombstone());

            // add the new map
            tx.mylayer.rangemapmgr.mapGenerationToRegion(tx, use_gen, start_key, end_key, reader);
        }
        // grow the top "top of heap"
        // .ROOT/FREELIST/HEAD -> "top of heap"
        private NewUnusedSegment growHeap(LayerWriteGroup tx, int length)
        {
            long new_addr;
            FreespaceExtent newblock_info;

            // make an atomic write-group to "carve off" a pending chunk
            LayerWriteGroup carveoff_wg =
                tx.mylayer.newWriteGroup(type: LayerWriteGroup.WriteGroupType.DISK_ATOMIC_NOFLUSH);

            // lock to make sure two threads don't carve off at the same time...
            lock (this) {
                // HACK: currently we just grab off the top of heap
                new_addr = next_allocation;
                next_allocation = next_allocation + (long)length;

                if (new_addr <= 0) {
                    throw new Exception("invalid address in allocateNewSegment: " + new_addr);
                }

                newblock_info = new FreespaceExtent();
                newblock_info.start_addr = new_addr;
                newblock_info.end_addr = new_addr + length;

                // add the pending chunk
                carveoff_wg.setValue(this.pendingKeyForAddr(new_addr),
                                     RecordUpdate.WithPayload(newblock_info.pack()));

                Console.WriteLine("allocateNewSegment - next address: " + new_addr);
                // add our new top of heap pointer
                {
                    RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/HEAD");
                    carveoff_wg.setValue(key, RecordUpdate.WithPayload(Lsd.numberToLsd(next_allocation, 13)));
                }

                // commit the metadata tx
                carveoff_wg.finish();
            }

            return new NewUnusedSegment(store,newblock_info);
        }
        // move the pending address into the freelist
        private void handleRegionSafeToFree(long start_addr, FreespaceExtent extent, LayerWriteGroup wg)
        {
            System.Console.WriteLine("*\n*\n*\n* handleRegionSafeToFree {0} \n*\n*\n*", start_addr);
            // (1) remove pending entry
            wg.setValue(pendingKeyForAddr(start_addr), RecordUpdate.DeletionTombstone());

            // (2) write real freelist entry (TODO: merge with neighboring entries)
            {
                RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/EXTENTS");
                key.appendKeyPart(new RecordKeyType_Long(extent.end_addr));
                wg.setValue(key, RecordUpdate.WithPayload(extent.pack()));
            }
            wg.finish();
        }
        public void freeSegment(LayerWriteGroup tx, FreespaceExtent segment_extent)
        {
            // (1) add the segment to the pending list (pending free)

            if (tx.type != LayerWriteGroup.WriteGroupType.DISK_ATOMIC_FLUSH) {
                throw new Exception("freeSegment() requires DISK_ATOMIC write group");
            }

            // NOTE: DISK_ATOMIC writes are not seen in the memory segment until the atomic write group applies
            //       so these changes will not be seen until then

            RecordKey key = new RecordKey().appendParsedKey(".ROOT/FREELIST/PENDING")
                .appendKeyPart(new RecordKeyType_Long(segment_extent.end_addr));

            RecordUpdate payload = RecordUpdate.WithPayload(segment_extent.pack());
            tx.setValue(key, payload);

            // (2) add a handler to get notified when the block is no longer referenced, so it can
            //     be moved from pending to actually free.

            LayerWriteGroup fwg = this.store.newWriteGroup(LayerWriteGroup.WriteGroupType.DISK_ATOMIC_NOFLUSH);

            // we don't want to ask for the notification until the write-group freeing this segment is finished
            tx.addCompletion(delegate() {
                tx.mylayer.regionmgr.notifyRegionSafeToFree(segment_extent.start_addr,
                    delegate(long addr) { this.handleRegionSafeToFree(addr, segment_extent, fwg); });
            });
        }