2 * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.vpnmanager;
10 import com.google.common.collect.ImmutableMap;
11 import com.google.common.util.concurrent.ThreadFactoryBuilder;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19 import java.util.concurrent.ExecutorService;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.ThreadFactory;
22 import javax.inject.Singleton;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * Aims to provide a common synchronization point for all those classes that
28 * want to know when certain type of Operational data is ready for a given VPN,
29 * and those others that can notify that the Operational data is ready.
32 public class VpnOpDataSyncer {
34 private static final Logger LOG = LoggerFactory.getLogger(VpnOpDataSyncer.class);
36 public enum VpnOpDataType {
41 // Maps VpnOpDataType to a Map of VpnName to a list of tasks to be executed once the the Vpn is fully ready.
42 private final Map<VpnOpDataType, ConcurrentMap<String, List<Runnable>>> mapOfMaps =
43 ImmutableMap.<VpnOpDataType, ConcurrentMap<String, List<Runnable>>>builder()
44 .put(VpnOpDataType.vpnInstanceToId, new ConcurrentHashMap<>())
45 .put(VpnOpDataType.vpnOpData, new ConcurrentHashMap<>())
49 private static final ThreadFactory THREAD_FACTORY =
50 new ThreadFactoryBuilder().setNameFormat("NV-VpnMgr-%d").build();
51 private final ExecutorService executorService = Executors.newSingleThreadExecutor(THREAD_FACTORY);
54 public boolean waitForVpnDataReady(VpnOpDataType vpnOpDataType, String vpnName, long maxWaitMillis,
57 boolean isDataReady = false;
60 isDataReady = waitForVpnDataReady(vpnOpDataType, vpnName, maxWaitMillis);
62 while (!isDataReady && attempts < maxAttempts);
67 // "Unconditional wait" and "Wait not in loop" wrt the VpnNotifyTask below - suppressing the FB violation -
68 // see comments below.
69 @SuppressFBWarnings({"UW_UNCOND_WAIT", "WA_NOT_IN_LOOP"})
70 public boolean waitForVpnDataReady(VpnOpDataType dataType, String vpnName, long maxWaitMillis) {
71 //TODO(vivek) This waiting business to be removed in carbon
72 boolean dataReady = false;
73 ConcurrentMap<String, List<Runnable>> listenerMap = mapOfMaps.get(dataType);
74 Runnable notifyTask = new VpnNotifyTask();
76 List<Runnable> notifyList = listenerMap.computeIfAbsent(vpnName,
77 k -> Collections.synchronizedList(new ArrayList<>()));
79 synchronized (notifyTask) {
80 // Per FB's "Unconditional wait" violation, the code should really verify that the condition it intends
81 // to wait for is not already satisfied before calling wait. However the VpnNotifyTask is published
82 // here while holding the lock on it so this path will hit the wait before notify can be invoked.
83 notifyList.add(notifyTask);
85 long t0 = System.nanoTime();
87 notifyTask.wait(maxWaitMillis);
88 long elapsedTimeNs = System.nanoTime() - t0;
90 if (elapsedTimeNs < maxWaitMillis * 1000000) {
91 // Thread woken up before timeout
92 LOG.debug("Its been reported that VPN {} is now ready", vpnName);
96 LOG.debug("Vpn {} OpData not ready after {}ms", vpnName, maxWaitMillis);
99 } catch (InterruptedException e) {
104 List<Runnable> notifyTaskList = listenerMap.get(vpnName);
105 if (notifyTaskList != null) {
106 synchronized (notifyTaskList) {
107 notifyTaskList.remove(notifyTask);
108 if (notifyTaskList.isEmpty()) {
109 listenerMap.remove(vpnName);
117 public void notifyVpnOpDataReady(VpnOpDataType dataType, String vpnName) {
118 LOG.debug("Reporting that vpn {} is ready", vpnName);
119 ConcurrentMap<String, List<Runnable>> listenerMap = mapOfMaps.get(dataType);
120 List<Runnable> notifyTaskList = listenerMap.remove(vpnName);
121 if (notifyTaskList == null) {
122 LOG.trace(" No notify tasks found for vpnName {}", vpnName);
126 Runnable[] notifyTasks;
127 synchronized (notifyTaskList) {
128 notifyTasks = notifyTaskList.toArray(new Runnable[notifyTaskList.size()]);
129 notifyTaskList.clear();
132 for (Runnable notifyTask : notifyTasks) {
133 executorService.execute(notifyTask);