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(final @Parameter(StageHandler.class) EventHandler<T> handler, 060 final @Parameter(NumberOfThreads.class) 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(final @Parameter(StageName.class) String name, 075 final @Parameter(StageHandler.class) EventHandler<T> handler, 076 final @Parameter(NumberOfThreads.class) int numThreads, 077 final @Parameter(ErrorHandler.class) 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 this.numThreads = numThreads; 084 this.executor = Executors.newFixedThreadPool(numThreads, new DefaultThreadFactory(name)); 085 StageManager.instance().register(this); 086 } 087 088 /** 089 * Constructs a thread-pool stage 090 * 091 * @param name the stage name 092 * @param handler the event handler to execute 093 * @param numThreads the number of threads to use 094 * @throws WakeRuntimeException 095 */ 096 @Inject 097 public ThreadPoolStage(final @Parameter(StageName.class) String name, 098 final @Parameter(StageHandler.class) EventHandler<T> handler, 099 final @Parameter(NumberOfThreads.class) int numThreads) { 100 this(name, handler, numThreads, null); 101 } 102 103 /** 104 * Constructs a thread-pool stage 105 * 106 * @param handler the event handler to execute 107 * @param executor the external executor service provided 108 */ 109 @Inject 110 public ThreadPoolStage(final @Parameter(StageHandler.class) EventHandler<T> handler, 111 final @Parameter(StageExecutorService.class) ExecutorService executor) { 112 this(handler.getClass().getName(), handler, executor); 113 } 114 115 116 /** 117 * Constructs a thread-pool stage 118 * 119 * @param handler the event handler to execute 120 * @param executor the external executor service provided 121 * @param errorHandler the error handler 122 */ 123 @Inject 124 public ThreadPoolStage(final @Parameter(StageHandler.class) EventHandler<T> handler, 125 final @Parameter(StageExecutorService.class) ExecutorService executor, 126 final @Parameter(ErrorHandler.class) EventHandler<Throwable> errorHandler) { 127 this(handler.getClass().getName(), handler, executor, errorHandler); 128 } 129 130 /** 131 * Constructs a thread-pool stage 132 * 133 * @param name the stage name 134 * @param handler the event handler to execute 135 * @param executor the external executor service provided 136 * for consistent tracking, it is recommended to create executor with {@link DefaultThreadFactory} 137 */ 138 @Inject 139 public ThreadPoolStage(final @Parameter(StageName.class) String name, 140 final @Parameter(StageHandler.class) EventHandler<T> handler, 141 final @Parameter(StageExecutorService.class) ExecutorService executor) { 142 this(name, handler, executor, null); 143 } 144 145 /** 146 * Constructs a thread-pool stage 147 * 148 * @param name the stage name 149 * @param handler the event handler to execute 150 * @param executor the external executor service provided 151 * for consistent tracking, it is recommended to create executor with {@link DefaultThreadFactory} 152 * @param errorHandler the error handler 153 */ 154 @Inject 155 public ThreadPoolStage(final @Parameter(StageName.class) String name, 156 final @Parameter(StageHandler.class) EventHandler<T> handler, 157 final @Parameter(StageExecutorService.class) ExecutorService executor, 158 final @Parameter(ErrorHandler.class) EventHandler<Throwable> errorHandler) { 159 super(name); 160 this.handler = handler; 161 this.errorHandler = errorHandler; 162 this.numThreads = 0; 163 this.executor = executor; 164 StageManager.instance().register(this); 165 } 166 167 /** 168 * Handles the event using a thread in the thread pool 169 * 170 * @param value the event 171 */ 172 @Override 173 public void onNext(final T value) { 174 beforeOnNext(); 175 executor.submit(new Runnable() { 176 177 @Override 178 public void run() { 179 try { 180 handler.onNext(value); 181 afterOnNext(); 182 } catch (Throwable t) { 183 if (errorHandler != null) { 184 errorHandler.onNext(t); 185 } else { 186 LOG.log(Level.SEVERE, name + " Exception from event handler", t); 187 throw t; 188 } 189 } 190 } 191 192 }); 193 } 194 195 /** 196 * Closes resources 197 * 198 * @return Exception 199 */ 200 @Override 201 public void close() throws Exception { 202 if (closed.compareAndSet(false, true)) { 203 if (numThreads > 0) { 204 executor.shutdown(); 205 if (!executor.awaitTermination(shutdownTimeout, TimeUnit.MILLISECONDS)) { 206 LOG.log(Level.WARNING, "Executor did not terminate in " + shutdownTimeout + "ms."); 207 List<Runnable> droppedRunnables = executor.shutdownNow(); 208 LOG.log(Level.WARNING, "Executor dropped " + droppedRunnables.size() + " tasks."); 209 } 210 } 211 } 212 } 213 214 /** 215 * Gets the queue length of this stage 216 * 217 * @return the queue length 218 */ 219 public int getQueueLength() { 220 return ((ThreadPoolExecutor) executor).getQueue().size(); 221 } 222 223}