All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gs.fw.common.mithra.cache.offheap.FastUnsafeOffHeapDataStorage Maven / Gradle / Ivy

There is a newer version: 18.1.0
Show newest version
/*
 Copyright 2016 Goldman Sachs.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */

package com.gs.fw.common.mithra.cache.offheap;


import com.gs.collections.api.iterator.IntIterator;
import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.map.mutable.primitive.IntLongHashMap;
import com.gs.collections.impl.set.mutable.primitive.IntHashSet;
import com.gs.fw.common.mithra.attribute.AsOfAttribute;
import com.gs.fw.common.mithra.attribute.Attribute;
import com.gs.fw.common.mithra.attribute.SingleColumnStringAttribute;
import com.gs.fw.common.mithra.cache.AbstractDatedCache;
import com.gs.fw.common.mithra.cache.ParallelProcedure;
import com.gs.fw.common.mithra.cache.ReadWriteLock;
import com.gs.fw.common.mithra.finder.RelatedFinder;
import com.gs.fw.common.mithra.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;

import java.io.IOException;
import java.io.ObjectOutput;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;

public class FastUnsafeOffHeapDataStorage implements OffHeapDataStorage
{
    private static final Logger logger = LoggerFactory.getLogger(FastUnsafeOffHeapDataStorage.class);
    public static final short PAGE_POWER_OF_TWO = 10;
    private static final int MAX_PAGES_TO_COPY_UNDER_LOCK = 10;
    private static Unsafe UNSAFE = MithraUnsafe.getUnsafe();
//    private static MithraUnsafe.AuditedMemory UNSAFE = MithraUnsafe.getAuditedMemory();
    private static OffHeapFreeThread LATER_FREE_THREAD = new OffHeapFreeThread();
    private static long FREE_STACK_HEAD_OFFSET;

    private int current = 2;
    private int max;
    private final long dataSize;
    private long baseAddress;
    private long totalAllocated;
    private long maxIncreaseSize;
    private int totalFreed;
    private volatile int fence;
    private Object[] dataArray;
    private ReferenceQueue weakRefQueue;
    private volatile boolean destroyed;
    private volatile long freeStackHeadAndSize = 0;
    protected ReadWriteLock readWriteLock; // made protected only for test
    protected final FastUnsafeOffHeapLongList pageVersionList = new FastUnsafeOffHeapLongList(1000); // made protected only for test
    protected long currentPageVersion = 0; // made protected only for test
    private final String businessClassName;
    private long maxReplicatedPageVersion = 0;
    private OffHeapStringExtractor[] stringAttributes;
    private OffHeapExtractor[] pkAttributes;
    private Constructor dataConstructor;
    private RelatedFinder finder;

    static
    {
        LATER_FREE_THREAD.start();

        Class storageClass = FastUnsafeOffHeapDataStorage.class;
        try
        {
            FREE_STACK_HEAD_OFFSET = MithraUnsafe.getUnsafe().objectFieldOffset(storageClass.getDeclaredField("freeStackHeadAndSize"));
        }
        catch (NoSuchFieldException e)
        {
            throw new RuntimeException("could not get freeStackHead field", e);
        }

    }

    public FastUnsafeOffHeapDataStorage(int dataSize, String businessClassName, RelatedFinder finder)
    {
        this.dataSize = dataSize;
        this.totalAllocated = this.dataSize << PAGE_POWER_OF_TWO; // one page
        this.maxIncreaseSize = this.dataSize << (PAGE_POWER_OF_TWO+10); // 1024 pages
        baseAddress = UNSAFE.allocateMemory(totalAllocated);
        UNSAFE.setMemory(baseAddress, totalAllocated, (byte) 0);
        UNSAFE.putByte(baseAddress, AbstractDatedCache.REMOVED_VERSION); // we don't store anything in address 0 or 1
        UNSAFE.putByte(baseAddress + dataSize, AbstractDatedCache.REMOVED_VERSION); // we don't store anything in address 0 or 1
        computeMax();
        this.dataArray = new Object[max+2];
        weakRefQueue = new ReferenceQueue();
        this.businessClassName = businessClassName;
        this.finder = finder;
        if (finder != null)
        {
            populateStringAttributes(finder);
            createDataConstructor(finder);
            populatePkAttributes(finder);
        }
    }

    private void createDataConstructor(RelatedFinder finder)
    {
        String finderClassName = finder.getFinderClassName();
        String qualifiedBusinessClassName = finderClassName.substring(0, finderClassName.length() - "Finder".length());
        String businessClassName = qualifiedBusinessClassName.substring(qualifiedBusinessClassName.lastIndexOf('.')+1);
        String dataClassName = qualifiedBusinessClassName +"Data$"+businessClassName+"DataOffHeap";
        try
        {
            this.dataConstructor = Class.forName(dataClassName).getConstructor(MutableInteger.class);
        }
        catch (Exception e)
        {
            throw new RuntimeException("could not find constructor for "+dataClassName, e);
        }
    }

    private void populateStringAttributes(RelatedFinder finder)
    {
        Attribute[] persistentAttributes = finder.getPersistentAttributes();
        List tmp = MithraFastList.newList();
        Attribute sourceAttribute = finder.getSourceAttribute();
        if (sourceAttribute != null && sourceAttribute instanceof SingleColumnStringAttribute)
        {
            tmp.add((OffHeapStringExtractor) sourceAttribute.zCreateOffHeapExtractor());
        }
        for(Attribute a: persistentAttributes)
        {
            if (a instanceof SingleColumnStringAttribute)
            {
                tmp.add((OffHeapStringExtractor) a.zCreateOffHeapExtractor());
            }
        }
        stringAttributes = tmp.toArray(new OffHeapStringExtractor[tmp.size()]);
    }

    private void populatePkAttributes(RelatedFinder finder)
    {
        List pkExtractorList = FastList.newList();
        Attribute[] pkAttributesWithoutProcessingFrom = finder.getPrimaryKeyAttributes();
        for(Attribute a: pkAttributesWithoutProcessingFrom)
        {
            pkExtractorList.add(a.zCreateOffHeapExtractor());
        }
        AsOfAttribute[] asOfAttributes = finder.getAsOfAttributes();
        for(AsOfAttribute a: asOfAttributes)
        {
            pkExtractorList.add(a.getFromAttribute().zCreateOffHeapExtractor());
        }
        this.pkAttributes = pkExtractorList.toArray(new OffHeapExtractor[pkExtractorList.size()]);
    }

    private MithraOffHeapDataObject createData(MutableInteger[] index)
    {
        try
        {
            return (MithraOffHeapDataObject) dataConstructor.newInstance(index);
        }
        catch (Exception e)
        {
            throw new RuntimeException("Could not instantiate data", e);
        }
    }

    public FastUnsafeOffHeapDataStorage(int dataSize, ReadWriteLock lock)
    {
        this(dataSize, "", null);
        this.readWriteLock = lock;
    }

    @Override
    public void setReadWriteLock(ReadWriteLock cacheLock)
    {
        this.readWriteLock = cacheLock;
    }

    @Override
    public synchronized boolean syncWithMasterCache(MasterCacheUplink uplink, OffHeapSyncableCache cache)
    {
        if (destroyed)
        {
            return true;
        }
        cache.setReplicationMode();
        MasterSyncResult result = null;
        try
        {
            SyncLog syncLog = new SyncLog(this.businessClassName);
            syncLog.setInitialMaxPageVersion(this.maxReplicatedPageVersion);
            result = uplink.syncWithMasterCache(this.businessClassName, maxReplicatedPageVersion);
            syncLog.logMasterResultReceived(result);
            if (destroyed)
            {
                logger.info("Stopping sync for "+this.businessClassName);
                return true;
            }
            result.fixUpStringReferences(this.stringAttributes, uplink, this.dataSize);
            syncLog.logStringsFixedUp();
            processIncomingSyncResult(result, cache, syncLog);
            syncLog.logSyncFinished();
            this.finder.getMithraObjectPortal().setLatestRefreshTime(result.getLastMasterRefreshTime());
            if (!result.isEmpty())
            {
                this.finder.getMithraObjectPortal().incrementClassUpdateCount();
            }
            syncLog.printLog();
            return false;
        }
        finally
        {
            if (result != null)
            {
                result.destroy();
            }
        }
    }

    private void processIncomingSyncResult(MasterSyncResult syncResult, OffHeapSyncableCache cache, SyncLog syncLog)
    {
        if (syncResult.getBuffers().isEmpty())
        {
            return;
        }
        int currentCopy = this.current;
        if (currentCopy == 2)
        {
            processInitialSync(syncResult, cache, syncLog);
        }
        else
        {
            IntLongHashMap pageLocationMap = syncResult.getPageLocationMap();
            FastList buffers = syncResult.getBuffers();
            int currentPage = this.current >> PAGE_POWER_OF_TWO;
            int maxPage = currentPage;

            for (IntIterator intIterator = pageLocationMap.keySet().intIterator(); intIterator.hasNext(); )
            {
                int pageIndex = intIterator.next();
                if (pageIndex > maxPage)
                {
                    maxPage = pageIndex;
                }
            }
            if (maxPage >= (max >> PAGE_POWER_OF_TWO))
            {
                processDataViaCopy(syncResult, maxPage, cache, syncLog);
            }
            else
            {
                processDataInPlace(syncResult, maxPage, cache, syncLog);
            }
        }
        this.maxReplicatedPageVersion = syncResult.getMaxReplicatedVersion();
        syncLog.setFinalMaxPageVersion(this.maxReplicatedPageVersion);
    }

    private void processInitialSync(MasterSyncResult syncResult, OffHeapSyncableCache cache, SyncLog syncLog)
    {
        syncLog.setInitialSync();
        FastList buffers = syncResult.getBuffers();
        if (buffers.size() == 1)
        {
            processInitialSyncWithoutCopy(cache, buffers.get(0));
        }
        else
        {
            processInitialSyncWithCopy(syncResult, cache, buffers);
        }
    }

    private void processInitialSyncWithCopy(MasterSyncResult syncResult, OffHeapSyncableCache cache, FastList buffers)
    {
        int pages = 0;
        for(int i=0;i> PAGE_POWER_OF_TWO;
                long pageFromSyncResult = pageLocationMap.get(page);
                int pageBufferLocation = syncResult.getPageBufferLocation(pageFromSyncResult);
                int bufferPageIndex = syncResult.getBufferPageIndex(pageFromSyncResult);
                FastUnsafeOffHeapPageBuffer buffer = buffers.get(pageBufferLocation);
                if (buffer.getUsedData().get(bufferPageIndex, dataIndex & ((1 << PAGE_POWER_OF_TWO)-1)))
                {
                    constructDataAndAddToCache(cache, constructorArg, dataIndex);
                }
            }
            this.current = dataCount - 1;
        }
        finally
        {
            this.readWriteLock.release();
        }
        LATER_FREE_THREAD.queue(oldBase);
    }

    private void checkBufferPages(FastList buffers, int pages, IntLongHashMap pageLocationMap)
    {
        for(int page=0;page> PAGE_POWER_OF_TWO;
        FastList buffers = syncResult.getBuffers();
        FastUnsafeOffHeapIntList toInsert = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toRemove = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toUpdate = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toNukeAndInsert = new FastUnsafeOffHeapIntList(1000);

        try
        {
            bucketAllIncomingData(currentMaxPage, buffers, toInsert, toRemove, toUpdate, toNukeAndInsert, syncLog);
            long newBase = copyExistingAndIncomingPagesIntoNew(syncResult, maxPage);
            MutableInteger[] constructorArg = createDataConstructorArg();
            this.readWriteLock.acquireWriteLock();
            try
            {
                for(int i=0;i> PAGE_POWER_OF_TWO;
        FastList buffers = syncResult.getBuffers();
        FastUnsafeOffHeapIntList toInsert = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toRemove = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toUpdate = new FastUnsafeOffHeapIntList(1000);
        FastUnsafeOffHeapIntList toNukeAndInsert = new FastUnsafeOffHeapIntList(1000);
        IntLongHashMap pageLocationMap = syncResult.getPageLocationMap();

        try
        {
            bucketAllIncomingData(currentMaxPage, buffers, toInsert, toRemove, toUpdate, toNukeAndInsert, syncLog);
            copyPagesAfterCurrent(syncResult, maxPage, getPageSize(), this.baseAddress, pageLocationMap, buffers, currentMaxPage, this.totalAllocated);
            MutableInteger[] constructorArg = createDataConstructorArg();
            this.readWriteLock.acquireWriteLock();
            try
            {
                for(int i=0;i buffers)
    {
        for (int i = 0; i < buffers.size(); i++)
        {
            FastUnsafeOffHeapPageBuffer buffer = buffers.get(i);
            FastUnsafeOffHeapIntList masterPageIndicies = buffer.getMasterPageIndicies();
            FastUnsafeOffHeapLongList masterPageVersions = buffer.getMasterPageVersions();

            for (int bufferPageIndex = 0; bufferPageIndex < masterPageVersions.size(); bufferPageIndex++)
            {
                int masterPageIndex = masterPageIndicies.get(bufferPageIndex);
                long masterPageVersion = masterPageVersions.get(bufferPageIndex);

                this.pageVersionList.set(masterPageIndex, masterPageVersion);
            }
        }
    }

    private void updateCacheAfterRemovalAndBufferCopy(MasterSyncResult syncResult, int maxPage, OffHeapSyncableCache cache,
            int currentMaxPage, FastList buffers, FastUnsafeOffHeapIntList toInsert,
            FastUnsafeOffHeapIntList toUpdate, FastUnsafeOffHeapIntList toNukeAndInsert,
            IntLongHashMap pageLocationMap, MutableInteger[] constructorArg)
    {
        for(int i=0;i buffers, IntLongHashMap pageLocationMap, int toCopyDataIndex)
    {
        int page = toCopyDataIndex >> PAGE_POWER_OF_TWO;
        int pageDataIndex = toCopyDataIndex & ((1 << PAGE_POWER_OF_TWO) - 1);
        long pageFromSyncResult = pageLocationMap.get(page);
        int pageBufferLocation = syncResult.getPageBufferLocation(pageFromSyncResult);
        int bufferPageIndex = syncResult.getBufferPageIndex(pageFromSyncResult);
        FastUnsafeOffHeapPageBuffer buffer = buffers.get(pageBufferLocation);
        long src = buffer.getPageStartLocation(bufferPageIndex) + pageDataIndex * dataSize;
        long dest = computeAddress(toCopyDataIndex, 0);
        assertedCopyMemory(src + 1, dest + 1, dataSize - 1, buffer.getBaseAddress(), buffer.getAllocatedLength(), this.baseAddress, this.totalAllocated);
        fence++;
        UNSAFE.putByte(dest, UNSAFE.getByte(src)); // we copy the dataVersion field last
    }

    private void bucketAllIncomingData(int currentMaxPage, FastList buffers, FastUnsafeOffHeapIntList toInsert,
            FastUnsafeOffHeapIntList toRemove, FastUnsafeOffHeapIntList toUpdate, FastUnsafeOffHeapIntList toNukeAndInsert, SyncLog syncLog)
    {
        for (int i = 0; i < buffers.size(); i++)
        {
            FastUnsafeOffHeapPageBuffer buffer = buffers.get(i);
            FastUnsafeOffHeapIntList masterPageIndicies = buffer.getMasterPageIndicies();
            for (int bufferPageIndex = 0; bufferPageIndex < masterPageIndicies.size(); bufferPageIndex++)
            {
                int page = masterPageIndicies.get(bufferPageIndex);
                if (page <= currentMaxPage)
                {
                    for (int pageDataIndex = 0; pageDataIndex < (1 << PAGE_POWER_OF_TWO); pageDataIndex++)
                    {
                        bucketIncomingData(toInsert, toRemove, toUpdate, toNukeAndInsert, buffer, bufferPageIndex, page, pageDataIndex);
                    }
                }
            }
        }
        syncLog.setToInsertInExistingPages(toInsert.size());
        syncLog.setToUpdateExisting(toUpdate.size());
        syncLog.setToRemoveExisting(toRemove.size());
        syncLog.setToNukeAndInsert(toNukeAndInsert.size());
    }

    private void constructDataAndAddToCache(OffHeapSyncableCache cache, MutableInteger[] constructorArg, int toAdd)
    {
        constructorArg[0].replace(toAdd);
        this.dataArray[toAdd] = createData(constructorArg);
        cache.syncDataAdd(this.dataArray[toAdd]);
        if (this.current < toAdd)
        {
            this.current = toAdd;
        }
    }

    private void bucketIncomingData(FastUnsafeOffHeapIntList toInsert, FastUnsafeOffHeapIntList toRemove,
            FastUnsafeOffHeapIntList toUpdate, FastUnsafeOffHeapIntList toNukeAndInsert,
            FastUnsafeOffHeapPageBuffer buffer, int bufferPageIndex, int page, int pageDataIndex)
    {
        int localDataIndex = (page <= 0;
        assert src >= srcBase;
        assert src + copySize <= srcBase + srcLength;
        assert dest >= destBase;
        assert dest + copySize <= destBase + destLength;
        UNSAFE.copyMemory(src, dest, copySize);
    }

    private boolean hasTheSamePk(int localDataIndex, FastUnsafeOffHeapPageBuffer buffer, int bufferPageIndex, int pageDataIndex)
    {
        for(OffHeapExtractor e: pkAttributes)
        {
            if (!e.valueEquals(this, localDataIndex, getBufferDataStartAddress(buffer, bufferPageIndex, pageDataIndex)))
            {
                return false;
            }
        }
        return true;
    }

    private long getBufferDataStartAddress(FastUnsafeOffHeapPageBuffer buffer, int bufferPageIndex, int pageDataIndex)
    {
        return buffer.getBaseAddress() + ((bufferPageIndex << PAGE_POWER_OF_TWO) + pageDataIndex) * dataSize;
    }

    private boolean isByteWiseDifferent(int localDataIndex, FastUnsafeOffHeapPageBuffer buffer, int bufferPageIndex, int pageDataIndex)
    {
        int numLongs = (int)(dataSize >> 3);
        int remainingBytes = (int) (dataSize & 7);
        long localPointer = computeAddress(localDataIndex, 0);
        long bufferPointer = getBufferDataStartAddress(buffer, bufferPageIndex, pageDataIndex);
        for(int i=0;i buffers = syncResult.getBuffers();
        int currentPage = this.current >> PAGE_POWER_OF_TWO;
        for(int page=0;page<=currentPage;page++)
        {
            long pageFromSyncResult = pageLocationMap.get(page);
            if (pageFromSyncResult == 0)
            {
                assertedCopyMemory(this.baseAddress + page * pageSize, newBase + page*pageSize, pageSize, this.baseAddress, this.totalAllocated, newBase, newSize);
            }
            else
            {
                copyBufferPage(syncResult, pageSize, newBase, buffers, page, pageFromSyncResult, newSize);
            }
        }
        copyPagesAfterCurrent(syncResult, maxPage, pageSize, newBase, pageLocationMap, buffers, currentPage, newSize);
        long remainder = newSize - (maxPage + 1) * pageSize;
        if (remainder > 0)
        {
            UNSAFE.setMemory(newBase + (maxPage+1)*pageSize, remainder, (byte) 0);
        }
        totalAllocated = newSize;
        return newBase;
    }

    private void copyPagesAfterCurrent(MasterSyncResult syncResult, int maxPage, long pageSize, long newBase,
            IntLongHashMap pageLocationMap, FastList buffers, int currentPage, long newSize)
    {
        for(int page=currentPage+1;page<=maxPage;page++)
        {
            long pageFromSyncResult = pageLocationMap.get(page);
            if (pageFromSyncResult == 0)
            {
                throw new RuntimeException("missing page after current page "+page);
            }
            else
            {
                copyBufferPage(syncResult, pageSize, newBase, buffers, page, pageFromSyncResult, newSize);
            }
        }

        int newCurrent = ((maxPage + 1) << PAGE_POWER_OF_TWO) - 1;
        if (newCurrent > this.current)
        {
            this.current = newCurrent;
        }
    }

    private void copyBufferPage(MasterSyncResult syncResult, long pageSize, long newBase, FastList buffers,
            int destinationPageIndex, long pageFromSyncResult, long newSize)
    {
        int pageBufferLocation = syncResult.getPageBufferLocation(pageFromSyncResult);
        int bufferPageIndex = syncResult.getBufferPageIndex(pageFromSyncResult);
        FastUnsafeOffHeapPageBuffer buffer = buffers.get(pageBufferLocation);
        assertedCopyMemory(buffer.getPageStartLocation(bufferPageIndex), newBase + destinationPageIndex * pageSize, pageSize,
                buffer.getBaseAddress(), buffer.getAllocatedLength(), newBase, newSize);
    }

    @Override
    public void markDataDirty(int dataOffset)
    {
        this.pageVersionList.set(dataOffset >> PAGE_POWER_OF_TWO, 0);
    }

    @Override
    public MasterSyncResult sendSyncResult(long maxReplicatedPageVersion)
    {
        return new MasterSyncResult(this, maxReplicatedPageVersion);
    }

    private void computeMax()
    {
        max = (int) (this.totalAllocated/dataSize);
    }

    private int getStackHead(long headAndSize)
    {
        return (int) (headAndSize & 0x7FFFFFF);
    }

    private int getStackSize(long headAndSize)
    {
        return (int) ((headAndSize >> 32) & 0x7FFFFFF);
    }

    private boolean casFreeStackHead(long expected, int newHead, int sizeIncrement)
    {
        long newValue = newHead | (((long)(getStackSize(expected) + sizeIncrement)) << 32);
        return UNSAFE.compareAndSwapLong(this, FREE_STACK_HEAD_OFFSET, expected, newValue);
    }

    @Override
    public int allocate(MithraOffHeapDataObject data)
    {
        while(true)
        {
            long curStackHead = freeStackHeadAndSize;
            int stackHead = getStackHead(curStackHead);
            if (stackHead != 0)
            {
                if (casFreeStackHead(curStackHead, getInt(stackHead, 0), -1))
                {
                    totalFreed--;
                    UNSAFE.setMemory(computeAddress(stackHead, 0), dataSize, (byte) 0);
                    dataArray[stackHead] = data;
                    markDataDirty(stackHead);
                    return stackHead;
                }
            }
            else
            {
                if (current+1 == max)
                {
                    reallocate();
                }
                dataArray[++current] = data;
                markDataDirty(current);
                return current;
            }
        }
    }

    private void reallocate()
    {
        long newSize = this.totalAllocated;
        if (totalAllocated < maxIncreaseSize)
        {
            newSize <<= 1;
        }
        else
        {
            newSize += maxIncreaseSize;
        }
        reallocate(newSize);
    }

    private void reallocate(long newSize)
    {
        assert newSize % getPageSize() == 0;
        long newBase = UNSAFE.allocateMemory(newSize);
        assert newSize >= totalAllocated;
        UNSAFE.copyMemory(this.baseAddress, newBase, this.totalAllocated);
        UNSAFE.setMemory(newBase + totalAllocated, newSize - totalAllocated, (byte) 0);
        long oldBase = this.baseAddress;
        this.baseAddress = newBase;
        totalAllocated = newSize;
        computeMax();
        dataArray = Arrays.copyOf(dataArray, max + 2);
        fence++; // ensure baseAddress is visible in other threads
        LATER_FREE_THREAD.queue(oldBase);
    }

    @Override
    public void free(int dataOffset)
    {
        WeakReferenceWithAddress weak = new WeakReferenceWithAddress(dataArray[dataOffset], weakRefQueue, dataOffset);
        dataArray[dataOffset] = weak;
        totalFreed++;
        markDataDirty(dataOffset);
    }

    @Override
    public boolean isBooleanNull(int dataOffset, int fieldOffset)
    {
        return UNSAFE.getByte(computeAddress(dataOffset, fieldOffset)) == 2;
    }

    @Override
    public void setBooleanNull(int dataOffset, int fieldOffset)
    {
        UNSAFE.putByte(computeAddress(dataOffset, fieldOffset), (byte) 2);
    }

    @Override
    public void destroy()
    {
        if (!destroyed)
        {
            UNSAFE.freeMemory(this.baseAddress);
            destroyed = true;
            this.baseAddress = -(1L << 40); // about a terrabyte
            this.dataArray = null;
            this.pageVersionList.destroy();
        }
    }

    @Override
    public boolean forAll(DoUntilProcedure procedure)
    {
        boolean done = false;
        int length = current + 1;
        for (int i = 2; i < length && !done; i++)
        {
            Object o = dataArray[i];
            if (o != null && !(o instanceof WeakReferenceWithAddress))
            {
                done = procedure.execute(o);
            }
        }
        return done;
    }

    @Override
    public void forAllInParallel(final ParallelProcedure procedure)
    {
        MithraCpuBoundThreadPool pool = MithraCpuBoundThreadPool.getInstance();
        int length = current + 1;
        ThreadChunkSize threadChunkSize = new ThreadChunkSize(pool.getThreads(), length, 1);
        final ArrayBasedQueue queue = new ArrayBasedQueue(length, threadChunkSize.getChunkSize());
        int threads = threadChunkSize.getThreads();
        procedure.setThreads(threads, length /threads);
        CpuBoundTask[] tasks = new CpuBoundTask[threads];
        for(int i=0;i 0 ? new FastUnsafeOffHeapIntList(100) : new FastUnsafeOffHeapIntList(pageVersionList.size());
        try
        {
            try
            {
                maxPageVersion = scanPagesToSend(maxClientReplicatedPageVersion, pagesToSend);
                if (pagesToSend.size() > 0 && pagesToSend.size() <= MAX_PAGES_TO_COPY_UNDER_LOCK)
                {
                    pageBuffer = new FastUnsafeOffHeapPageBuffer(getPageSize(), pagesToSend.size(), pagesToSend);
                    for(int i=0;i maxClientReplicatedPageVersion)
        {
            pagesToSend.add(pageNum);
        }
        if (pageVersion > maxPageVersion)
        {
            maxPageVersion = pageVersion;
        }
        return maxPageVersion;
    }

    private void sendPagesInBatches(long maxClientReplicatedPageVersion, long maxPageVersion, FastUnsafeOffHeapIntList pagesToSend, ObjectOutput out)
            throws IOException
    {
        FastUnsafeOffHeapPageBuffer pageBuffer = new FastUnsafeOffHeapPageBuffer(getPageSize(), MAX_PAGES_TO_COPY_UNDER_LOCK, new FastUnsafeOffHeapIntList(MAX_PAGES_TO_COPY_UNDER_LOCK));
        int offset = 0;
        try
        {
            while(offset < pagesToSend.size())
            {
                boolean restart = false;
                this.readWriteLock.acquireReadLock();
                try
                {
                    int end = MAX_PAGES_TO_COPY_UNDER_LOCK;
                    if (offset + end >= pagesToSend.size())
                    {
                        end = pagesToSend.size() - offset;
                    }
                    // ensure version numbers are still ok
                    for(int i=0;i maxPageVersion)
                        {
                            IntHashSet setOfPagesToSend = new IntHashSet(pagesToSend.size());
                            for(int j=offset;j> PAGE_POWER_OF_TWO) + 1;
                    if (initialSync)
                    {
                        buffer.append("initial sync of ").append(pageCount).append(" data pages");
                    }
                    else
                    {
                        if (processInPlace)
                        {
                            buffer.append("in place processed records\n");
                        }
                        else
                        {
                            buffer.append("processed via copy\n");
                            buffer.append("final page count ").append(pageCount).append("\n");
                        }
                        appendIfNonZero(buffer, toInsertInExistingPages, " inserted in existing pages\n");
                        appendIfNonZero(buffer, toRemoveExisting, " removed\n");
                        appendIfNonZero(buffer, toUpdateExisting, " updated\n");
                        appendIfNonZero(buffer, toNukeAndInsert, " replaced with different\n");
                    }
                }
                logger.debug(buffer.toString());
            }
        }

        private void appendIfNonZero(StringBuffer buffer, int num, String msg)
        {
            if (num > 0)
            {
                buffer.append(num).append(msg);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy