/** * This software is GPLv2. * Take a look at the LICENSE file for more info. */ package de.tu.dresden.dud.dc.WorkCycle; import java.io.IOException; import java.security.SecureRandom; import java.util.Iterator; import java.util.LinkedList; import org.apache.log4j.Logger; import de.tu.dresden.dud.dc.Util; import de.tu.dresden.dud.dc.ManagementMessage.ManagementMessageAdd; /** * @author klobs * * In this class the reservation phase is managed. It is just a special * case of the normal sending phase. * */ public class WorkCycleReserving extends WorkCycleSending { // Logging private static Logger log = Logger.getLogger(WorkCycleReserving.class); private LinkedList<Integer> actualRoundsCalculated = new LinkedList<Integer>(); private short myRandomNumber = 0; /** * @param r * the work cycle which the reservation belongs to */ public WorkCycleReserving(WorkCycle r) { super(r); expectedRounds = -1; // do we actually need to reserve? if (r.hasPayload()) { SecureRandom n = new SecureRandom(); myRandomNumber = (short) n.nextInt(Short.MAX_VALUE); } } @Override protected void addUp() { byte[] b = null; b = new byte[WorkCycleReservationPayload.RESERVATION_PAYLOAD_SIZE]; Iterator<byte[]> i = payloads.iterator(); while (i.hasNext()) { byte[] c = i.next(); b = Util.mergeDCwise(b, c, WorkCycleSending.MODULUS); } payloadSend = b; } private void performDCReservationParticipantSide() { int desiredMessageLength = getSystemPayloadLength(); if (assocWorkCycleManag.getMessageLengthMode() == WorkCycleManager.MESSAGE_LENGTHS_VARIABLE) { desiredMessageLength = (assocWorkCycle.getNextPayload().length % 4 == 0) ? assocWorkCycle .getNextPayload().length : assocWorkCycle.getNextPayload().length + (4 - (assocWorkCycle.getNextPayload().length % 4)); } WorkCycleReservationPayload a = null; WorkCycleReservationPayload rp = new WorkCycleReservationPayload(desiredMessageLength, myRandomNumber); ManagementMessageAdd m = null; // collision detection boolean collisiondetected = false; int collisionsuspected = 0; WorkCycleReservationPayload collisionpayload = null; // Rounds, wait and waited counter int rc = 0; int wait = 0; int waited = 0; while (!finished) { // Do we want to send our random number in this reservation? // This is only the case if we have actually something to send at all, // and (it is the first step of reservation or our random number is <= the mean value). if ((wait == 0) && (relativeRound < 0) && (this.assocWorkCycle.hasPayload()) && !collisiondetected) { // Yes, we do want, so prepare the message with the right keys. byte[] p = Util.mergeDCwise( rp.getPayload(), assocKeyGenerator .calcKeys( WorkCycleReservationPayload.RESERVATION_PAYLOAD_SIZE, workcycleNumber, rc), WorkCycleSending.MODULUS); m = new ManagementMessageAdd(workcycleNumber, rc, p); waited = 0; } else { // no, so only an empty message with the keys has to be sent. byte[] p = assocKeyGenerator.calcKeys( WorkCycleReservationPayload.RESERVATION_PAYLOAD_SIZE, workcycleNumber, rc); m = new ManagementMessageAdd(workcycleNumber, rc, p); } // send the message. try { getAssocParticipantManager().getMyInfo().getAssocConnection().sendMessage(m.getMessage()); } catch (IOException o) { log.error(o.toString()); } // lock the semaphore, because we need to wait for the reaction // to continue try{ assocWorkCycle.getSemaphore().acquire(); } catch (InterruptedException e) { log.error(e.toString()); } // The semaphore has be unlocked form a different thread (the producer, the work cycle) // Now we can fetch the long awaited response and continue the algorithm. if(assocWorkCycle.getAddedMessages().size() > 0){ a = new WorkCycleReservationPayload(assocWorkCycle.getAddedMessages().removeFirst().getPayload()); } else { finished = true; break; } // get the expected count of steps for the reservation, if not done, yet // (so only the 1st time. if (expectedRounds < 0) expectedRounds = a.getParticipantCount(); if(!assocWorkCycle.hasPayload()){ wait = 1; if(a.getParticipantCount() == 1){ expectedRounds--; actualRoundsCalculated.add(a.getDesiredPayloadLength()); } } else if (myRandomNumber <= a.getAverage() && relativeRound < 0) { // left side of the branch // Current average is my random number and I am the only sender if ((a.getParticipantCount() == 1) && (myRandomNumber == a.getAverage())) { relativeRound = actualRoundsCalculated.size(); actualRoundsCalculated.add(a.getDesiredPayloadLength()); expectedRounds--; } // Current average is my random sum, but there are more people trying to send it -> collusion // Either we do not send, or we take a look at the desiredPayloadlength() TODO else if ((a.getParticipantCount() > 1) && (myRandomNumber == a.getAverage())) { if (rc == collisionsuspected + 1){ if (collisionpayload.getParticipantCount() == a.getParticipantCount() && collisionpayload.getAverage() == a.getAverage()){ collisiondetected = true; expectedRounds = expectedRounds - a.getParticipantCount(); } } collisionsuspected = rc; collisionpayload = a; } wait = 0; } else { if (waited == 1) wait = a.getParticipantCount(); else if (waited == 0) wait = 1; waited++; //Did somebody else find the slot? if(a.getParticipantCount() == 1){ expectedRounds--; actualRoundsCalculated.add(a.getDesiredPayloadLength()); wait--; } else if (collisionpayload != null && collisionpayload.getAverage() == a.getAverage() && collisionpayload.getParticipantCount() == a.getParticipantCount()){ expectedRounds = expectedRounds - a.getParticipantCount(); wait = wait - a.getParticipantCount(); } collisionpayload = a; } // inc Round count rc++; // Do we want to send in the next reservation round? finished = (expectedRounds == 0); } expectedRounds = actualRoundsCalculated.size(); if (collisiondetected == true) relativeRound = -1; // Reservation has finished. Notify the observers! setChanged(); notifyObservers(WorkCycle.WC_SENDING); } public LinkedList<Integer> getIndividualMessageLengths(){ return actualRoundsCalculated; } @Override public void run(){ performDCReservationParticipantSide(); } }