001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.util.concurrent; 016 017import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; 018 019import com.google.common.annotations.GwtIncompatible; 020import com.google.common.annotations.J2ktIncompatible; 021import com.google.errorprone.annotations.CanIgnoreReturnValue; 022import java.time.Duration; 023import java.util.concurrent.Executor; 024import java.util.concurrent.TimeUnit; 025import java.util.concurrent.TimeoutException; 026 027/** 028 * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown} 029 * methods. This class uses a single thread to execute the service; consider {@link AbstractService} 030 * if you would like to manage any threading manually. 031 * 032 * @author Jesse Wilson 033 * @since 1.0 034 */ 035@GwtIncompatible 036@J2ktIncompatible 037@ElementTypesAreNonnullByDefault 038public abstract class AbstractExecutionThreadService implements Service { 039 /* use AbstractService for state management */ 040 private final Service delegate = 041 new AbstractService() { 042 @Override 043 protected final void doStart() { 044 Executor executor = MoreExecutors.renamingDecorator(executor(), () -> serviceName()); 045 executor.execute( 046 () -> { 047 try { 048 startUp(); 049 notifyStarted(); 050 // If stopAsync() is called while starting we may be in the STOPPING state in 051 // which case we should skip right down to shutdown. 052 if (isRunning()) { 053 try { 054 AbstractExecutionThreadService.this.run(); 055 } catch (Throwable t) { 056 restoreInterruptIfIsInterruptedException(t); 057 try { 058 shutDown(); 059 } catch (Exception ignored) { 060 restoreInterruptIfIsInterruptedException(ignored); 061 t.addSuppressed(ignored); 062 } 063 notifyFailed(t); 064 return; 065 } 066 } 067 068 shutDown(); 069 notifyStopped(); 070 } catch (Throwable t) { 071 restoreInterruptIfIsInterruptedException(t); 072 notifyFailed(t); 073 } 074 }); 075 } 076 077 @Override 078 protected void doStop() { 079 triggerShutdown(); 080 } 081 082 @Override 083 public String toString() { 084 return AbstractExecutionThreadService.this.toString(); 085 } 086 }; 087 088 /** Constructor for use by subclasses. */ 089 protected AbstractExecutionThreadService() {} 090 091 /** 092 * Start the service. This method is invoked on the execution thread. 093 * 094 * <p>By default this method does nothing. 095 */ 096 protected void startUp() throws Exception {} 097 098 /** 099 * Run the service. This method is invoked on the execution thread. Implementations must respond 100 * to stop requests. You could poll for lifecycle changes in a work loop: 101 * 102 * <pre> 103 * public void run() { 104 * while ({@link #isRunning()}) { 105 * // perform a unit of work 106 * } 107 * } 108 * </pre> 109 * 110 * <p>...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which 111 * should cause {@link #run()} to return. 112 */ 113 protected abstract void run() throws Exception; 114 115 /** 116 * Stop the service. This method is invoked on the execution thread. 117 * 118 * <p>By default this method does nothing. 119 */ 120 // TODO: consider supporting a TearDownTestCase-like API 121 protected void shutDown() throws Exception {} 122 123 /** 124 * Invoked to request the service to stop. 125 * 126 * <p>By default this method does nothing. 127 * 128 * <p>Currently, this method is invoked while holding a lock. If an implementation of this method 129 * blocks, it can prevent this service from changing state. If you need to performing a blocking 130 * operation in order to trigger shutdown, consider instead registering a listener and 131 * implementing {@code stopping}. Note, however, that {@code stopping} does not run at exactly the 132 * same times as {@code triggerShutdown}. 133 */ 134 protected void triggerShutdown() {} 135 136 /** 137 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 138 * this method to use a custom {@link Executor}, which may configure its worker thread with a 139 * specific name, thread group or priority. The returned executor's {@link 140 * Executor#execute(Runnable) execute()} method is called when this service is started, and should 141 * return promptly. 142 * 143 * <p>The default implementation returns a new {@link Executor} that sets the name of its threads 144 * to the string returned by {@link #serviceName} 145 */ 146 protected Executor executor() { 147 return command -> MoreExecutors.newThread(serviceName(), command).start(); 148 } 149 150 @Override 151 public String toString() { 152 return serviceName() + " [" + state() + "]"; 153 } 154 155 @Override 156 public final boolean isRunning() { 157 return delegate.isRunning(); 158 } 159 160 @Override 161 public final State state() { 162 return delegate.state(); 163 } 164 165 /** @since 13.0 */ 166 @Override 167 public final void addListener(Listener listener, Executor executor) { 168 delegate.addListener(listener, executor); 169 } 170 171 /** @since 14.0 */ 172 @Override 173 public final Throwable failureCause() { 174 return delegate.failureCause(); 175 } 176 177 /** @since 15.0 */ 178 @CanIgnoreReturnValue 179 @Override 180 public final Service startAsync() { 181 delegate.startAsync(); 182 return this; 183 } 184 185 /** @since 15.0 */ 186 @CanIgnoreReturnValue 187 @Override 188 public final Service stopAsync() { 189 delegate.stopAsync(); 190 return this; 191 } 192 193 /** @since 15.0 */ 194 @Override 195 public final void awaitRunning() { 196 delegate.awaitRunning(); 197 } 198 199 /** @since 28.0 */ 200 @Override 201 public final void awaitRunning(Duration timeout) throws TimeoutException { 202 Service.super.awaitRunning(timeout); 203 } 204 205 /** @since 15.0 */ 206 @Override 207 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 208 delegate.awaitRunning(timeout, unit); 209 } 210 211 /** @since 15.0 */ 212 @Override 213 public final void awaitTerminated() { 214 delegate.awaitTerminated(); 215 } 216 217 /** @since 28.0 */ 218 @Override 219 public final void awaitTerminated(Duration timeout) throws TimeoutException { 220 Service.super.awaitTerminated(timeout); 221 } 222 223 /** @since 15.0 */ 224 @Override 225 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 226 delegate.awaitTerminated(timeout, unit); 227 } 228 229 /** 230 * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name 231 * in debugging output. 232 * 233 * <p>Subclasses may override this method. 234 * 235 * @since 14.0 (present in 10.0 as getServiceName) 236 */ 237 protected String serviceName() { 238 return getClass().getSimpleName(); 239 } 240}