001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.wake.impl; 020 021import org.apache.reef.tang.annotations.Parameter; 022import org.apache.reef.wake.AbstractEStage; 023import org.apache.reef.wake.EventHandler; 024import org.apache.reef.wake.StageConfiguration.*; 025import org.apache.reef.wake.WakeParameters; 026import org.apache.reef.wake.exception.WakeRuntimeException; 027 028import javax.inject.Inject; 029import java.util.List; 030import java.util.concurrent.ExecutorService; 031import java.util.concurrent.Executors; 032import java.util.concurrent.ThreadPoolExecutor; 033import java.util.concurrent.TimeUnit; 034import java.util.logging.Level; 035import java.util.logging.Logger; 036 037/** 038 * Stage that executes an event handler with a thread pool. 039 * 040 * @param <T> type 041 */ 042public final class ThreadPoolStage<T> extends AbstractEStage<T> { 043 private static final Logger LOG = Logger.getLogger(ThreadPoolStage.class.getName()); 044 045 private final EventHandler<T> handler; 046 private final ExecutorService executor; 047 private final int numThreads; 048 private final long shutdownTimeout = WakeParameters.EXECUTOR_SHUTDOWN_TIMEOUT; 049 private final EventHandler<Throwable> errorHandler; 050 051 /** 052 * Constructs a thread-pool stage. 053 * 054 * @param handler the event handler to execute 055 * @param numThreads the number of threads to use 056 * @throws WakeRuntimeException 057 */ 058 @Inject 059 public ThreadPoolStage(@Parameter(StageHandler.class) final EventHandler<T> handler, 060 @Parameter(NumberOfThreads.class) final int numThreads) { 061 this(handler.getClass().getName(), handler, numThreads, null); 062 } 063 064 /** 065 * Constructs a thread-pool stage. 066 * 067 * @param name the stage name 068 * @param handler the event handler to execute 069 * @param numThreads the number of threads to use 070 * @param errorHandler the error handler 071 * @throws WakeRuntimeException 072 */ 073 @Inject 074 public ThreadPoolStage(@Parameter(StageName.class) final String name, 075 @Parameter(StageHandler.class) final EventHandler<T> handler, 076 @Parameter(NumberOfThreads.class) final int numThreads, 077 @Parameter(ErrorHandler.class) final EventHandler<Throwable> errorHandler) { 078 super(name); 079 this.handler = handler; 080 this.errorHandler = errorHandler; 081 if (numThreads <= 0) { 082 throw new WakeRuntimeException(name + " numThreads " + numThreads + " is less than or equal to 0"); 083 } 084 this.numThreads = numThreads; 085 this.executor = Executors.newFixedThreadPool(numThreads, new DefaultThreadFactory(name)); 086 StageManager.instance().register(this); 087 } 088 089 /** 090 * Constructs a thread-pool stage. 091 * 092 * @param name the stage name 093 * @param handler the event handler to execute 094 * @param numThreads the number of threads to use 095 * @throws WakeRuntimeException 096 */ 097 @Inject 098 public ThreadPoolStage(@Parameter(StageName.class) final String name, 099 @Parameter(StageHandler.class) final EventHandler<T> handler, 100 @Parameter(NumberOfThreads.class) final int numThreads) { 101 this(name, handler, numThreads, null); 102 } 103 104 /** 105 * Constructs a thread-pool stage. 106 * 107 * @param handler the event handler to execute 108 * @param executor the external executor service provided 109 */ 110 @Inject 111 public ThreadPoolStage(@Parameter(StageHandler.class) final EventHandler<T> handler, 112 @Parameter(StageExecutorService.class) final ExecutorService executor) { 113 this(handler.getClass().getName(), handler, executor); 114 } 115 116 117 /** 118 * Constructs a thread-pool stage. 119 * 120 * @param handler the event handler to execute 121 * @param executor the external executor service provided 122 * @param errorHandler the error handler 123 */ 124 @Inject 125 public ThreadPoolStage(@Parameter(StageHandler.class) final EventHandler<T> handler, 126 @Parameter(StageExecutorService.class) final ExecutorService executor, 127 @Parameter(ErrorHandler.class) final EventHandler<Throwable> errorHandler) { 128 this(handler.getClass().getName(), handler, executor, errorHandler); 129 } 130 131 /** 132 * Constructs a thread-pool stage. 133 * 134 * @param name the stage name 135 * @param handler the event handler to execute 136 * @param executor the external executor service provided 137 * for consistent tracking, it is recommended to create executor with {@link DefaultThreadFactory} 138 */ 139 @Inject 140 public ThreadPoolStage(@Parameter(StageName.class) final String name, 141 @Parameter(StageHandler.class) final EventHandler<T> handler, 142 @Parameter(StageExecutorService.class) final ExecutorService executor) { 143 this(name, handler, executor, null); 144 } 145 146 /** 147 * Constructs a thread-pool stage. 148 * 149 * @param name the stage name 150 * @param handler the event handler to execute 151 * @param executor the external executor service provided 152 * for consistent tracking, it is recommended to create executor with {@link DefaultThreadFactory} 153 * @param errorHandler the error handler 154 */ 155 @Inject 156 public ThreadPoolStage(@Parameter(StageName.class) final String name, 157 @Parameter(StageHandler.class) final EventHandler<T> handler, 158 @Parameter(StageExecutorService.class) final ExecutorService executor, 159 @Parameter(ErrorHandler.class) final EventHandler<Throwable> errorHandler) { 160 super(name); 161 this.handler = handler; 162 this.errorHandler = errorHandler; 163 this.numThreads = 0; 164 this.executor = executor; 165 StageManager.instance().register(this); 166 } 167 168 /** 169 * Handles the event using a thread in the thread pool. 170 * 171 * @param value the event 172 */ 173 @Override 174 public void onNext(final T value) { 175 beforeOnNext(); 176 executor.submit(new Runnable() { 177 178 @Override 179 public void run() { 180 try { 181 handler.onNext(value); 182 afterOnNext(); 183 } catch (final Throwable t) { 184 if (errorHandler != null) { 185 errorHandler.onNext(t); 186 } else { 187 LOG.log(Level.SEVERE, name + " Exception from event handler", t); 188 throw t; 189 } 190 } 191 } 192 193 }); 194 } 195 196 /** 197 * Closes resources. 198 * 199 * @return Exception 200 */ 201 @Override 202 public void close() throws Exception { 203 if (closed.compareAndSet(false, true)) { 204 if (numThreads > 0) { 205 executor.shutdown(); 206 if (!executor.awaitTermination(shutdownTimeout, TimeUnit.MILLISECONDS)) { 207 LOG.log(Level.WARNING, "Executor did not terminate in " + shutdownTimeout + "ms."); 208 final List<Runnable> droppedRunnables = executor.shutdownNow(); 209 LOG.log(Level.WARNING, "Executor dropped " + droppedRunnables.size() + " tasks."); 210 } 211 } 212 } 213 } 214 215 /** 216 * Gets the queue length of this stage. 217 * 218 * @return the queue length 219 */ 220 public int getQueueLength() { 221 return ((ThreadPoolExecutor) executor).getQueue().size(); 222 } 223 224}