/**
* 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();
}
}
}