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.Name; 022import org.apache.reef.tang.annotations.NamedParameter; 023import org.apache.reef.tang.annotations.Parameter; 024import org.apache.reef.wake.Stage; 025import org.apache.reef.wake.WakeParameters; 026 027import javax.inject.Inject; 028import java.util.List; 029import java.util.concurrent.ForkJoinPool; 030import java.util.concurrent.ForkJoinTask; 031import java.util.concurrent.TimeUnit; 032import java.util.concurrent.atomic.AtomicBoolean; 033import java.util.logging.Level; 034import java.util.logging.Logger; 035 036 037// This implementation uses the fork join framework to reduce the cost of spawning 038// events in stages. For two participating stages back to back, the pool allows 039// for the thread in the first stage to execute the event it submits to the second stage. 040// These choices are made by the ForkJoinPool. 041// 042// So, this does sort of go against the reason for stages, but doesn't eliminate them 043// and raises the level of abstraction that Wake sees above threads. 044// 045// this will only be deadlock free if blocking synchronization done by events is safe. 046// That is no event submitted to the pool can have a producer/consumer dependency 047// on another event submitted to the pool 048public class WakeSharedPool implements Stage { 049 private static final Logger LOG = Logger.getLogger(WakeSharedPool.class.getName()); 050 051 // not a constant, so specify here 052 private static final int DEFAULT_PARALLELISM = Math.max(1, Runtime.getRuntime().availableProcessors() - 2); 053 private final ForkJoinPool pool; 054 private final long shutdownTimeout = WakeParameters.EXECUTOR_SHUTDOWN_TIMEOUT; 055 private AtomicBoolean closed = new AtomicBoolean(false); 056 057 @Inject 058 public WakeSharedPool(@Parameter(Parallelism.class) final int parallelism) { 059 this.pool = new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, 060 new Thread.UncaughtExceptionHandler() { 061 @Override 062 public void uncaughtException(final Thread t, final Throwable e) { 063 // TODO: need to pass this upwards to REEF can grab it 064 } 065 }, 066 // async mode turned on so a task that invokes other tasks does not have to join on them. 067 // this is appropriate for event-based tasks, where once you submit an event to a stage it 068 // is always fire-and-forget. 069 true); 070 071 // register it with the StageManager, since the pool is meant to back stages 072 StageManager.instance().register(this); 073 } 074 075 // TODO do we need this? 076 //public ForkJoinPool pool() { 077 // return pool; 078 //} 079 080 @Inject 081 public WakeSharedPool() { 082 this(DEFAULT_PARALLELISM); 083 } 084 085 public void submit(final ForkJoinTask<?> t) { 086 if (ForkJoinTask.inForkJoinPool()) { 087 ForkJoinTask.invokeAll(t); 088 // alternatively just pool().pool.execute(t), which simply forces it to be this pool 089 // (right now we expect only one anyway) 090 } else { 091 pool.submit(t); 092 } 093 } 094 095 @Override 096 public void close() throws Exception { 097 LOG.info("ending pool stage: " + pool.toString()); 098 if (closed.compareAndSet(false, true)) { 099 pool.shutdown(); 100 if (!pool.awaitTermination(shutdownTimeout, TimeUnit.MILLISECONDS)) { 101 LOG.log(Level.WARNING, "Executor did not terminate in " + shutdownTimeout + "ms."); 102 final List<Runnable> droppedRunnables = pool.shutdownNow(); 103 LOG.log(Level.WARNING, "Executor dropped " + droppedRunnables.size() + " tasks."); 104 } 105 } 106 } 107 108 @NamedParameter 109 private static class Parallelism implements Name<Integer> { 110 } 111}