/** * Copyright 2013, Landz and its contributors. All rights reserved. * * 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 z.channel.contrast; import sun.misc.Contended; import static z.util.Contracts.contract; import static z.util.Unsafes.*; /** * {@link ContendedHyperLoop} is an Inter-Thread-Communication(ITC) mechanism. * <P> * Note:<p> * now, the LongHyperLoop aims to support Landz's Asyncor async programming * model. For performance, the current implementations of LongHyperLoop use * off-heap memory via s.m.Unsafe, so it is a little "heavy", take care * when you rush with "new/discard" to LongHyperLoop. * <p> see more * */ public class ContendedHyperLoop { //TODO: add a config option for SIZE_HYPERLOOP_BASE? private static final int SIZE_HYPERLOOP_BASE = 8*1024;//? private static final int SIZE_SHIFT_BUFFERSLOT = 3; private final long addressRaw; private final long addressHyperLoop; private final int nBufferSlots; private final int bufferSlotMask; // private final long addrWriteCursor; private final long addrBuffer;//buffer area grows up private final long addrReadCursorCount;//int type private long addrReadCursors;//readCursor area grows down @Contended private long writeCursor = 0; @Contended private long minReadCursor = 0; /** * create a LongHyperLoop. * <p> * contract: <p> * 1. nBufferSlots > 0 <p> * 2. nBufferSlots is a power of 2 <p> * * @param nBufferSlots the size of internal buffer in slot unit */ public ContendedHyperLoop(int nBufferSlots) { contract(()->nBufferSlots>0); contract(()->Integer.bitCount(nBufferSlots)==1); this.nBufferSlots = nBufferSlots; this.bufferSlotMask = nBufferSlots - 1; //======================================================== int requestedSize = SIZE_HYPERLOOP_BASE + (nBufferSlots<< SIZE_SHIFT_BUFFERSLOT); addressRaw = systemAllocateMemory(requestedSize + SIZE_CACHE_LINE); addressHyperLoop = nextCacheLineAlignedAddress(addressRaw); contract(()-> isCacheLineAligned(addressHyperLoop)); this.addrBuffer = addressHyperLoop + SIZE_CACHE_LINE_PADDING; this.addrReadCursorCount = addressHyperLoop + requestedSize - SIZE_CACHE_LINE_PADDING; this.addrReadCursors = addrReadCursorCount;// - SIZE_CACHE_LINE_PADDING; UNSAFE.putInt(addrReadCursorCount,0); } /** * TODO: overload a long array version?? * TODO: overload buffer based version?? * @param value * @return */ public void sendTo(long value) { while ((writeCursor - minReadCursor) == nBufferSlots) { // assume readCursorCount>0 minReadCursor = UNSAFE.getLongVolatile(null, addrReadCursors); for (int i = 1; i < UNSAFE.getInt(addrReadCursorCount); i++) { long readCursor = UNSAFE.getLongVolatile(null, addrReadCursors + i * SIZE_CACHE_LINE_PADDING); minReadCursor = minReadCursor > readCursor ? readCursor : minReadCursor; } long tries = 600; while (0!=tries) { tries--; } } // UNSAFE.putLong( // addrBuffer + ((writeCursor & bufferSlotMask) << SIZE_SHIFT_BUFFERSLOT), // value); writeCursor++; UNSAFE.storeFence(); } @Override public void finalize() { systemFreeMemory(addressRaw); } /** * Note: * it is always hoped the values sent into LongHyperLoop could be consumed ASAP. * * TODO: need to handle the removal of OutPort dynamically */ public final class Out { private final long addrReadCursor; public Out() { synchronized(Out.class) { addrReadCursors -= SIZE_CACHE_LINE_PADDING; this.addrReadCursor = addrReadCursors; UNSAFE.putInt(addrReadCursorCount, UNSAFE.getInt(addrReadCursorCount) + 1); } } public void receive() { long readCursor = UNSAFE.getLongVolatile(null,addrReadCursor); while (readCursor==writeCursor) { Thread.yield(); // long tries = 600; // while (0!=tries) { // tries--; // } } // long value = UNSAFE.getLong( // addrBuffer + ((readCursor & bufferSlotMask)<<SIZE_SHIFT_BUFFERSLOT)); UNSAFE.putLong(addrReadCursor, readCursor+1); UNSAFE.storeFence(); } } }