/* * Copyright (c) 2008-2017, Hazelcast, Inc. 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 com.hazelcast.util.concurrent; import java.util.concurrent.locks.LockSupport; import static com.hazelcast.util.Preconditions.checkNotNegative; import static java.lang.Long.numberOfLeadingZeros; import static java.lang.Long.parseLong; import static java.lang.Math.min; import static java.lang.String.format; /** * Idling strategy for threads when they have no work to do. * <p/> * Spin for maxSpins, then * {@link Thread#yield()} for maxYields, then * {@link LockSupport#parkNanos(long)} on an exponential backoff to maxParkPeriodNs */ public class BackoffIdleStrategy implements IdleStrategy { private static final int ARG_COUNT = 5; private static final int ARG_MAX_SPINS = 1; private static final int ARG_MAX_YIELDS = 2; private static final int ARG_MIN_PARK_PERIOD = 3; private static final int ARG_MAX_PARK_PERIOD = 4; final long yieldThreshold; final long parkThreshold; final long minParkPeriodNs; final long maxParkPeriodNs; private final int maxShift; /** * Create a set of state tracking idle behavior * * @param maxSpins to perform before moving to {@link Thread#yield()} * @param maxYields to perform before moving to {@link LockSupport#parkNanos(long)} * @param minParkPeriodNs to use when initiating parking * @param maxParkPeriodNs to use when parking */ public BackoffIdleStrategy(long maxSpins, long maxYields, long minParkPeriodNs, long maxParkPeriodNs) { checkNotNegative(maxSpins, "maxSpins must be positive or zero"); checkNotNegative(maxYields, "maxYields must be positive or zero"); checkNotNegative(minParkPeriodNs, "minParkPeriodNs must be positive or zero"); checkNotNegative(maxParkPeriodNs - minParkPeriodNs, "maxParkPeriodNs must be greater than or equal to minParkPeriodNs"); this.yieldThreshold = maxSpins; this.parkThreshold = maxSpins + maxYields; this.minParkPeriodNs = minParkPeriodNs; this.maxParkPeriodNs = maxParkPeriodNs; this.maxShift = numberOfLeadingZeros(minParkPeriodNs) - numberOfLeadingZeros(maxParkPeriodNs); } /** * {@inheritDoc} */ @Override public boolean idle(long n) { if (n < yieldThreshold) { return false; } if (n < parkThreshold) { Thread.yield(); return false; } final long parkTime = parkTime(n); LockSupport.parkNanos(parkTime); return parkTime == maxParkPeriodNs; } long parkTime(long n) { final long proposedShift = n - parkThreshold; final long allowedShift = min(maxShift, proposedShift); return proposedShift > maxShift ? maxParkPeriodNs : proposedShift < maxShift ? minParkPeriodNs << allowedShift : min(minParkPeriodNs << allowedShift, maxParkPeriodNs); } /** * Creates a new BackoffIdleStrategy. */ public static BackoffIdleStrategy createBackoffIdleStrategy(String config) { String[] args = config.split(","); if (args.length != ARG_COUNT) { throw new IllegalArgumentException( format("Invalid backoff configuration '%s', 4 arguments expected", config)); } long maxSpins = parseLong(args[ARG_MAX_SPINS]); long maxYields = parseLong(args[ARG_MAX_YIELDS]); long minParkPeriodNs = parseLong(args[ARG_MIN_PARK_PERIOD]); long maxParkNanos = parseLong(args[ARG_MAX_PARK_PERIOD]); return new BackoffIdleStrategy(maxSpins, maxYields, minParkPeriodNs, maxParkNanos); } }