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

com.lithium.flow.util.LoopThread Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Lithium Technologies, Inc.
 *
 * 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.lithium.flow.util;

import static com.google.common.base.Preconditions.checkNotNull;

import com.lithium.flow.config.Config;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.slf4j.Logger;

/**
 * @author Matt Ayres
 */
public class LoopThread extends Thread {
	private static final Logger log = Logs.getLogger();
	private static final AtomicInteger nextId = new AtomicInteger();

	private final long interval;
	private final Executable executable;
	private final Consumer onException;
	private long nextTime;
	private volatile boolean doFinish;
	private volatile boolean finished;

	public LoopThread(@Nonnull Executable executable) {
		this(0, executable);
	}

	public LoopThread(long interval, @Nonnull Executable executable) {
		this(interval, 0, false, executable);
	}

	public LoopThread(long interval, long offset, boolean round, @Nonnull Executable executable) {
		this(null, interval, offset, round, executable, exception -> log.warn("loop iteration failed", exception));
	}

	public LoopThread(@Nullable String name, long interval, long offset, boolean round,
			@Nonnull Executable executable, @Nonnull Consumer onException) {
		super(name != null ? name : "LoopThread-" + nextId.incrementAndGet());
		this.interval = interval;
		this.executable = checkNotNull(executable);
		this.onException = checkNotNull(onException);

		long time = System.currentTimeMillis();
		nextTime = time + offset;
		if (round && interval > 0) {
			nextTime += interval - time % interval;
		}

		setDaemon(true);
		start();
	}

	@Override
	public void run() {
		while (!interrupted() && !doFinish) {
			if (!Sleep.until(nextTime)) {
				break;
			}

			try {
				executable.call();
			} catch (Exception e) {
				onException.accept(e);
			}

			if (interval > 0) {
				nextTime += interval;

				long time = System.currentTimeMillis();
				if (time > nextTime) {
					while (time > nextTime) {
						nextTime += interval;
					}
					nextTime -= interval;
				}
			}
		}

		finished = true;
	}

	/**
	 * @deprecated Use {@link #close()} instead.
	 */
	@Deprecated
	public void finish() {
		close();
	}

	public void close() {
		close(-1);
	}

	public boolean close(long timeout) {
		long endTime = timeout == -1 ? Long.MAX_VALUE : System.currentTimeMillis() + timeout;

		doFinish = true;
		Sleep.until(() -> finished || System.currentTimeMillis() >= endTime);

		return finished;
	}

	@Nonnull
	public static LoopThread from(@Nonnull Config config, @Nonnull Executable executable) {
		long interval = config.getTime("loopInterval");
		long offset = config.getTime("loopOffset", "0");
		boolean round = config.getBoolean("loopRound", false);
		return new LoopThread(interval, offset, round, executable);
	}

	@Nonnull
	public static Builder builder() {
		return new Builder();
	}

	public static class Builder {
		private String name;
		private long interval;
		private long offset;
		private boolean round;
		private Executable executable;
		private Consumer onException;

		@Nonnull
		public Builder setName(@Nonnull String name) {
			this.name = checkNotNull(name);
			return this;
		}

		@Nonnull
		public Builder interval(long interval) {
			this.interval = interval;
			return this;
		}

		@Nonnull
		public Builder offset(long offset) {
			this.offset = offset;
			return this;
		}

		@Nonnull
		public Builder round(boolean round) {
			this.round = round;
			return this;
		}

		@Nonnull
		public Builder execute(@Nonnull Executable executable) {
			this.executable = checkNotNull(executable);
			return this;
		}

		@Nonnull
		public Builder onException(@Nonnull Consumer onException) {
			this.onException = checkNotNull(onException);
			return this;
		}

		@Nonnull
		public LoopThread build() {
			return new LoopThread(name, interval, offset, round, executable, onException);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy