/*
 * Copyright 2016-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onlab.util;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.concurrent.Executors.newScheduledThreadPool;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * Utility for managing a set of shared execution resources, such as a single
 * thread scheduled executor and thread pool scheduled executor for use by
 * various parts of the platform or by applications.
 * <p>
 * Whenever possible, use of these shared resources is encouraged over creating
 * separate ones.
 * </p>
 */
public final class SharedScheduledExecutors {

    public static final int DEFAULT_POOL_SIZE = 30;

    private static SharedScheduledExecutorService singleThreadExecutor = null;

    private static SharedScheduledExecutorService poolThreadExecutor = null;

    // Ban public construction
    private SharedScheduledExecutors() {
    }

    private static void setup() {
        if (singleThreadExecutor == null) {
            singleThreadExecutor =
                new SharedScheduledExecutorService(
                    newSingleThreadScheduledExecutor(
                        groupedThreads("onos/shared/scheduled",
                            "onos-single-executor")));

            poolThreadExecutor =
                new SharedScheduledExecutorService(
                    newScheduledThreadPool(DEFAULT_POOL_SIZE,
                        groupedThreads("onos/shared/scheduled",
                            "onos-pool-executor-%d")));
        }
    }

    /**
     * Returns the shared scheduled single thread executor.
     *
     * @return shared scheduled single thread executor
     */
    public static SharedScheduledExecutorService getSingleThreadExecutor() {
        setup();
        return singleThreadExecutor;
    }

    /**
     * Executes one-shot timer task on shared thread pool.
     *
     * @param task timer task to execute
     * @param delay before executing the task
     * @param unit of delay
     * @return a ScheduledFuture representing pending completion of the task
     *         and whose get() method will return null upon completion
     */
    public static ScheduledFuture<?> newTimeout(Runnable task, long delay, TimeUnit unit) {
        setup();
        return SharedScheduledExecutors.getPoolThreadExecutor()
                .schedule(task, delay, unit);
    }

    /**
     * Returns the shared scheduled thread pool executor.
     *
     * @return shared scheduled executor pool
     */
    public static SharedScheduledExecutorService getPoolThreadExecutor() {
        setup();
        return poolThreadExecutor;
    }

    /**
     * Configures the shared scheduled thread pool size.
     *
     * @param poolSize new pool size
     */
    public static void setPoolSize(int poolSize) {
        setup();
        checkArgument(poolSize > 0, "Shared pool size size must be greater than 0");
        poolThreadExecutor.setBackingExecutor(
                newScheduledThreadPool(poolSize, groupedThreads("onos/shared/scheduled",
                                                                "onos-pool-executor-%d")));
    }

    /**
     * Shuts down all shared scheduled executors.
     * This is not intended to be called by application directly.
     */
    public static void shutdown() {
        singleThreadExecutor.backingExecutor().shutdown();
        singleThreadExecutor = null;
        poolThreadExecutor.backingExecutor().shutdown();
        poolThreadExecutor = null;
    }
}

@@ -1413,16 +1413,79 @@
  = 30;%0A%0A
+    private static final Object EXECUTORS_LOCK = new Object();%0A
     priv
@@ -1551,25 +1551,24 @@
 tor = null;%0A
-%0A
     private 
@@ -1742,24 +1742,68 @@
 d setup() %7B%0A
+        synchronized (EXECUTORS_LOCK) %7B%0A    
         if (
@@ -1838,32 +1838,36 @@
 ) %7B%0A            
+    
 singleThreadExec
@@ -1881,32 +1881,36 @@
                 
+ 
+   
 new SharedSchedu
@@ -1921,32 +1921,36 @@
 xecutorService(%0A
+    
                 
@@ -2003,32 +2003,36 @@
                 
+    
 groupedThreads(%22
@@ -2079,24 +2079,28 @@
             
+  
+  
 %22onos-single
@@ -2115,16 +2115,20 @@
 r%22)));%0A%0A
+    
         
@@ -2160,32 +2160,36 @@
                 
+    
 new SharedSchedu
@@ -2220,32 +2220,36 @@
                 
+  
+  
 newScheduledThre
@@ -2274,16 +2274,20 @@
 L_SIZE,%0A
+    
         
@@ -2362,32 +2362,36 @@
                 
+    
 %22onos-pool-execu
@@ -2398,24 +2398,38 @@
 tor-%25d%22)));%0A
+            %7D%0A
         %7D%0A  
@@ -4210,24 +4210,68 @@
 hutdown() %7B%0A
+        synchronized (EXECUTORS_LOCK) %7B%0A    
         sing
@@ -4317,32 +4317,36 @@
 down();%0A        
+    
 singleThreadExec
@@ -4358,32 +4358,36 @@
 = null;%0A        
+  
+  
 poolThreadExecut
@@ -4419,32 +4419,36 @@
 down();%0A        
+    
 poolThreadExecut
@@ -4454,20 +4454,30 @@
 tor = null;%0A
+        %7D%0A
     %7D%0A%7D%0A

Synchronize access to static executors