001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2019 nobark (tools4j), Marco Terzer, Anton Anufriev 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.tools4j.nobark.run; 025 026import java.util.Collections; 027import java.util.List; 028import java.util.Objects; 029import java.util.concurrent.ThreadFactory; 030import java.util.concurrent.TimeUnit; 031import java.util.concurrent.atomic.AtomicInteger; 032 033import sun.misc.Contended; 034 035/** 036 * A thread that performs a main {@link java.lang.Runnable runnable} in a new thread and another shutdown runnable the 037 * graceful {@link #shutdown} phase of the thread. The thread is started immediately upon construction. 038 */ 039public class ShutdownableThread implements ThreadLike, Shutdownable { 040 041 private static final int RUNNING = 0; 042 private static final int SHUTDOWN = 1; 043 private static final int SHUTDOWN_NOW = 2; 044 045 private final RunnableFactory mainRunnableFactory; 046 private final RunnableFactory shutdownRunnableFactory; 047 private final Thread thread; 048 @Contended 049 private final AtomicInteger state = new AtomicInteger(RUNNING); 050 051 /** 052 * Constructor for shutdownable thread; it is recommended to use the static start(..) methods instead. 053 * 054 * @param mainRunnableFactory the factory for the main runnable; 055 * the <i>{@link #keepMainRunning}</i> condition is passed to the factory as lambda 056 * @param shutdownRunnableFactory the factory for the shutdown phase runnable; 057 * the <i>{@link #keepShutdownRunning}</i> condition is passed to the factory as lambda 058 * @param threadFactory the factory to provide the thread 059 */ 060 protected ShutdownableThread(final RunnableFactory mainRunnableFactory, 061 final RunnableFactory shutdownRunnableFactory, 062 final ThreadFactory threadFactory) { 063 this.mainRunnableFactory = Objects.requireNonNull(mainRunnableFactory); 064 this.shutdownRunnableFactory = Objects.requireNonNull(shutdownRunnableFactory); 065 this.thread = threadFactory.newThread(this::run); 066 thread.start(); 067 } 068 069 /** 070 * Creates, starts and returns a new shutdownable thread. 071 * 072 * @param mainRunnableFactory the factory for the main runnable; 073 * the <i>{@link #keepMainRunning}</i> condition is passed to the factory as lambda 074 * @param shutdownRunnableFactory the factory for the shutdown phase runnable; 075 * the <i>{@link #keepShutdownRunning}</i> condition is passed to the factory as lambda 076 * @param threadFactory the factory to provide the thread 077 * @return the newly created and started shutdownable thread 078 */ 079 public static ShutdownableThread start(final RunnableFactory mainRunnableFactory, 080 final RunnableFactory shutdownRunnableFactory, 081 final ThreadFactory threadFactory) { 082 return new ShutdownableThread(mainRunnableFactory, shutdownRunnableFactory, threadFactory); 083 } 084 085 private void run() { 086 final Runnable main = mainRunnableFactory.create(this::keepMainRunning); 087 final Runnable shutdown = shutdownRunnableFactory.create(this::keepShutdownRunning); 088 main.run(); 089 shutdown.run(); 090 } 091 092 @Override 093 public void shutdown() { 094 state.compareAndSet(RUNNING, SHUTDOWN); 095 } 096 097 098 @Override 099 public List<Runnable> shutdownNow() { 100 final int shutdownAndNow = SHUTDOWN | SHUTDOWN_NOW; 101 if (!state.compareAndSet(RUNNING, shutdownAndNow)) { 102 state.compareAndSet(SHUTDOWN, shutdownAndNow); 103 } 104 return Collections.emptyList(); 105 } 106 107 private boolean keepMainRunning() { 108 return (state.get() & SHUTDOWN) == 0; 109 } 110 111 private boolean keepShutdownRunning() { 112 return (state.get() & SHUTDOWN_NOW) == 0; 113 } 114 115 @Override 116 public boolean isShutdown() { 117 return (state.get() & SHUTDOWN) != 0; 118 } 119 120 @Override 121 public boolean isTerminated() { 122 return threadState() == Thread.State.TERMINATED; 123 } 124 125 @Override 126 public boolean awaitTermination(final long timeout, final TimeUnit unit) { 127 if (timeout < 0) { 128 throw new IllegalArgumentException("timeout value is negative: " + timeout); 129 } 130 if (isTerminated()) { 131 return true; 132 } 133 final long millis = unit.toMillis(timeout); 134 final long nanos = unit.toNanos(timeout - unit.convert(millis, TimeUnit.MILLISECONDS)); 135 try { 136 if (nanos == 0) { 137 thread.join(millis); 138 } else { 139 thread.join(millis, (int) nanos); 140 } 141 } catch (final InterruptedException e) { 142 throw new IllegalStateException("Join interrupted for thread " + thread); 143 } 144 return isTerminated(); 145 } 146 147 @Override 148 public Thread.State threadState() { 149 return thread.getState(); 150 } 151 152 /** 153 * Calls {@link #shutdown()}. 154 */ 155 @Override 156 public void stop() { 157 shutdown(); 158 } 159 160 /** 161 * Delegates to {@link #awaitTermination(long, TimeUnit)}. 162 */ 163 @Override 164 public void join(final long millis) { 165 awaitTermination(millis, TimeUnit.MILLISECONDS); 166 } 167 168 /** 169 * Returns the name of the thread that was created with the thread factory passed to the constructor. 170 * 171 * @return the service thread's name 172 * @see Thread#getName() 173 */ 174 @Override 175 public String toString() { 176 return thread.getName(); 177 } 178}