Bug 8346 - Conflicting modification for vpnNextHops.
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnOpDataSyncer.java
1 /*
2  * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.vpnmanager;
9
10 import com.google.common.collect.ImmutableMap;
11 import com.google.common.util.concurrent.ThreadFactoryBuilder;
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.ThreadFactory;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Aims to provide a common synchronization point for all those classes that
25  * want to know when certain type of Operational data is ready for a given VPN,
26  * and those others that can notify that the Operational data is ready.
27  */
28 public class VpnOpDataSyncer {
29
30     static final Logger LOG = LoggerFactory.getLogger(VpnOpDataSyncer.class);
31
32     public enum VpnOpDataType {
33         vpnInstanceToId,
34         vpnOpData,
35     }
36
37     // Maps a VpnName with a list of Task to be executed once the the Vpn is fully ready.
38     private final ConcurrentHashMap<String, List<Runnable>> vpnInst2IdSynchronizerMap = new ConcurrentHashMap<>();
39     private final ConcurrentHashMap<String, List<Runnable>> vpnInst2OpDataSynchronizerMap = new ConcurrentHashMap<>();
40
41     // The only purpose of this map is being able to reuse code
42     private final Map<VpnOpDataType, ConcurrentHashMap<String, List<Runnable>>> mapOfMaps =
43         ImmutableMap.<VpnOpDataType, ConcurrentHashMap<String, List<Runnable>>>builder()
44             .put(VpnOpDataType.vpnInstanceToId, vpnInst2IdSynchronizerMap)
45             .put(VpnOpDataType.vpnOpData,       vpnInst2OpDataSynchronizerMap)
46             .build();
47
48
49     private static final ThreadFactory THREAD_FACTORY =
50         new ThreadFactoryBuilder().setNameFormat("NV-VpnMgr-%d").build();
51     private final ExecutorService executorService = Executors.newSingleThreadExecutor(THREAD_FACTORY);
52
53
54     public boolean waitForVpnDataReady(VpnOpDataType vpnOpDataType, String vpnName, long maxWaitMillis,
55                                        int maxAttempts) {
56         int attempts = 0;
57         boolean isDataReady = false;
58         do {
59             attempts++;
60             isDataReady = waitForVpnDataReady(vpnOpDataType, vpnName, maxWaitMillis);
61         }
62         while (!isDataReady && attempts < maxAttempts);
63
64         return isDataReady;
65     }
66
67     public boolean waitForVpnDataReady(VpnOpDataType dataType, String vpnName, long maxWaitMillis) {
68         //TODO(vivek) This waiting business to be removed in carbon
69         boolean dataReady = false;
70         ConcurrentHashMap<String, List<Runnable>> listenerMap = mapOfMaps.get(dataType);
71         Runnable notifyTask = new VpnNotifyTask();
72         List<Runnable> notifieeList = null;
73         try {
74             synchronized (listenerMap) {
75                 listenerMap.computeIfAbsent(vpnName, k -> new ArrayList<>()).add(notifyTask);
76             }
77
78             synchronized (notifyTask) {
79                 long t0 = System.nanoTime();
80                 long elapsedTimeNs = t0;
81                 try {
82
83                     notifyTask.wait(maxWaitMillis);
84                     elapsedTimeNs = System.nanoTime() - t0;
85
86                     if (elapsedTimeNs < (maxWaitMillis * 1000000)) {
87                         // Thread woken up before timeout
88                         LOG.debug("Its been reported that VPN {} is now ready", vpnName);
89                         dataReady = true;
90                     } else {
91                         // Timeout
92                         LOG.debug("Vpn {} OpData not ready after {}ms", vpnName, maxWaitMillis);
93                         dataReady = false;
94                     }
95                 } catch (InterruptedException e) {
96                     dataReady = true;
97                 }
98             }
99         } finally {
100             synchronized (listenerMap) {
101                 notifieeList = listenerMap.get(vpnName);
102                 if (notifieeList != null) {
103                     notifieeList.remove(notifyTask);
104                     if (notifieeList.isEmpty()) {
105                         listenerMap.remove(vpnName);
106                     }
107                 }
108             }
109         }
110         return dataReady;
111     }
112
113     public void notifyVpnOpDataReady(VpnOpDataType dataType, String vpnName) {
114         LOG.debug("Reporting that vpn {} is ready", vpnName);
115         ConcurrentHashMap<String, List<Runnable>> listenerMap = mapOfMaps.get(dataType);
116         synchronized (listenerMap) {
117             List<Runnable> notifieeList = listenerMap.remove(vpnName);
118             if (notifieeList == null) {
119                 LOG.trace(" No notify tasks found for vpnName {}", vpnName);
120                 return;
121             }
122             Iterator<Runnable> notifieeIter = notifieeList.iterator();
123             while (notifieeIter.hasNext()) {
124                 Runnable notifyTask = notifieeIter.next();
125                 executorService.execute(notifyTask);
126                 notifieeIter.remove();
127             }
128         }
129     }
130 }