package ch.usi.da.paxos.old;
/*
* Copyright (c) 2013 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* URingPaxos is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.message.Message;
import ch.usi.da.paxos.message.MessageType;
import ch.usi.da.paxos.storage.Promise;
/**
* Name: ProposerReserver<br>
* Description: <br>
*
* Creation date: Apr 10, 2012<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class ProposerReserver implements Runnable {
private final Proposer proposer;
private final DatagramChannel channel;
private ByteBuffer buffer = ByteBuffer.allocate(8192);
private final List<DatagramPacket> out = new ArrayList<DatagramPacket>();
private final Selector selector;
private Majority majority;
private long instance;
private int ballot;
private int bcounter;
private long start;
/**
* Public constructor
*
* @param proposer
* @throws IOException
*/
public ProposerReserver(Proposer proposer) throws IOException{
this.proposer = proposer;
NetworkInterface i = NetworkInterface.getByName(Configuration.getInterface());
this.channel = DatagramChannel.open(StandardProtocolFamily.INET)
.setOption(StandardSocketOptions.SO_REUSEADDR, true)
.bind(Configuration.getGroup(PaxosRole.Proposer))
.setOption(StandardSocketOptions.IP_MULTICAST_IF, i);
this.channel.configureBlocking(false);
this.channel.join(Configuration.getGroup(PaxosRole.Proposer).getAddress(), i);
selector = Selector.open();
}
@Override
public void run() {
while(proposer.isLeader()){
try {
instance = proposer.getInstance().incrementAndGet();
ballot = 0;
bcounter = 0;
boolean promise = false;
while(!promise){
// new ballot = 'ballot counter' + 'ID'
ballot = 10*bcounter+proposer.getID();
// send 1a
Message m = new Message(instance,proposer.getID(),PaxosRole.Acceptor,MessageType.Prepare,ballot,null);
byte[] b = Message.toWire(m);
DatagramPacket packet = new DatagramPacket(b,b.length,Configuration.getGroup(m.getReceiver()));
out.add(packet);
channel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
// wait 1b majority
majority = new Majority();
start = System.currentTimeMillis();
while (!majority.isQuorum() && (System.currentTimeMillis() - start < 2000)){
selector.select(1000);
Set<SelectionKey> keys = selector.selectedKeys();
synchronized (keys){
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
it.remove();
if (!key.isValid())
continue;
if (key.isWritable()){
write(key);
}
if (key.isReadable()){
read(key);
}
}
}
}
bcounter++;
promise = majority.isQuorum();
}
if(!proposer.isPerfTest()){
System.out.println("reserve ballot " + ballot + " in instance " + instance);
}
proposer.getPromiseQueue().put(new Promise(new Long(instance),new Integer(ballot)));
} catch (InterruptedException e) {
} catch (IOException e) {
e.printStackTrace();
}
}
try {
selector.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void read(SelectionKey key){
DatagramChannel channel = (DatagramChannel)key.channel();
try{
buffer.clear();
SocketAddress address = channel.receive(buffer);
if (address == null)
return;
buffer.flip();
int count = buffer.remaining();
if (count > 0){
byte[] bytes = new byte[count];
buffer.get(bytes);
DatagramPacket in = new DatagramPacket(bytes, count, address);
Message m = Message.fromWire(in.getData());
//System.out.println("receive " + m);
if(m != null){
if(m.getType() == MessageType.Nack && m.getInstance() == instance){
start = 0; // skip waiting
}else if(m.getType() == MessageType.Promise && m.getInstance() == instance){
if(m.getValue() == null && m.getBallot() >= ballot){
majority.addMessage(m);
}else if (m.getValue() != null){
instance = proposer.getInstance().incrementAndGet();
ballot = 0;
bcounter = 0;
start = 0;
//re-send 2a
Message resend = new Message(m.getInstance(),proposer.getID(),PaxosRole.Acceptor,MessageType.Accept,m.getBallot(),m.getValue());
byte[] b = Message.toWire(resend);
DatagramPacket packet = new DatagramPacket(b,b.length,Configuration.getGroup(m.getReceiver()));
out.add(packet);
key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
}
}
}
}
selector.wakeup();
}catch (IOException e){
e.printStackTrace();
}
}
private void write(SelectionKey key){
DatagramChannel channel = (DatagramChannel)key.channel();
try {
while (!out.isEmpty()){
DatagramPacket packet = (DatagramPacket)out.get(0);
buffer.clear();
if(packet != null){
buffer.put(packet.getData());
buffer.flip();
channel.send(buffer, packet.getSocketAddress());
if (buffer.hasRemaining())
return;
}
out.remove(0);
}
key.interestOps(SelectionKey.OP_READ);
selector.wakeup();
}catch (IOException e){
e.printStackTrace();
}
}
}