/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltcore.agreement; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.voltcore.logging.VoltLogger; import org.voltcore.messaging.VoltMessage; import org.voltcore.utils.CoreUtils; public class FakeMesh extends Thread { private static VoltLogger meshLog = new VoltLogger("FAKEMESH"); public static class Message { public final long m_src; public final long m_dest; public final VoltMessage m_msg; public final boolean m_close; public Message(long src, long dest, VoltMessage msg) { m_src = src; m_dest = dest; m_msg = msg; m_close = false; } public Message(long src, long dest, boolean close) { m_src = src; m_dest = dest; m_msg = null; m_close = true; } } private Map<Long, Queue<Message>> m_sendQs = new HashMap<Long, Queue<Message>>(); private Map<Long, Queue<Message>> m_recvQs = new HashMap<Long, Queue<Message>>(); private Map<Long, Set<Long>> m_failedLinks = new HashMap<Long, Set<Long>>(); private AtomicBoolean m_shutdown = new AtomicBoolean(false); FakeMesh() { } synchronized void registerNode(long HSId, Queue<Message> sendQ, Queue<Message> recvQ) { if (m_sendQs.containsKey(HSId) || m_recvQs.containsKey(HSId)) { meshLog.error("Queue already registered for HSID: " + HSId + ", bailing"); return; } m_sendQs.put(HSId, sendQ); m_recvQs.put(HSId, recvQ); } synchronized void unregisterNode(long HSId) { meshLog.info("Unregistering HSId: " + CoreUtils.hsIdToString(HSId)); m_sendQs.remove(HSId); m_recvQs.remove(HSId); } synchronized void failLink(long src, long dst) { if (internalFailLink(src, dst)) { meshLog.info("Failing link between source: " + CoreUtils.hsIdToString(src) + " and destination: " + CoreUtils.hsIdToString(dst)); } } synchronized private boolean internalFailLink(long src,long dst) { Set<Long> dsts = m_failedLinks.get(src); if (dsts == null) { dsts = new HashSet<Long>(); m_failedLinks.put(src, dsts); } return dsts.add(dst); } synchronized void closeLink(long src, long dst) { meshLog.info("Close link between source: " + CoreUtils.hsIdToString(src) + " and destination: " + CoreUtils.hsIdToString(dst)); if (m_recvQs.containsKey(dst)) { m_recvQs.get(dst).offer(new Message(src,dst,true)); } internalFailLink(src,dst); internalFailLink(dst,src); } private boolean linkFailed(long src, long dst) { Set<Long> dsts = m_failedLinks.get(src); if (dsts == null || !dsts.contains(dst)) { return false; } return true; } void shutdown() { m_shutdown.set(true); } @Override public void start() { setName("FakeMesh"); super.start(); } @Override public void run() { while (!m_shutdown.get()) { // blunt-force trauma concurrency synchronized(this) { for (Entry<Long, Queue<Message>> sq : m_sendQs.entrySet()) { Message msg = sq.getValue().poll(); if (msg != null) { if (m_recvQs.containsKey(msg.m_dest) && !linkFailed(msg.m_src, msg.m_dest)) { m_recvQs.get(msg.m_dest).offer(msg); } } } } } } }