def __init__(self, id, name, participationCounter, clock): self.Id = id self.CoordinatorProcessId = None self.Name = name self.ParticipationCounter = participationCounter self.DefaultClock = clock self.Clock = clock self.Status = BullyProcessStatus.Killed self.sharedData = None self.DSSocket = None self.timer = DSTimer(2, self.timer_elapsed)
def __init__(self, id, isCoordinator): self.Id = id self.IsCoordinator = isCoordinator self.Data = None self.DSStatus = DSProcessStatus.Stopped self.DSSocket = None self.CurrentCoordinatorTransaction = None self.CurrentParticipantOperation = None self.CoordinatorTransactionLog = [] self.OperationTransactionLog = [] self.flipParticipantAcknowledge = False self.timeFailure = False self.timeFailureTimeout = 0 self.timer = DSTimer(1, self.timer_elapsed)
class DSTimeout: def __init__(self, timeout): self.timeout = timeout self.handler = None self.timer = DSTimer(1, self.timer_elapsed) self.passedSeconds = 0 def Run(self, handler): self.handler = handler self.timer.Restart() def timer_elapsed(self): self.passedSeconds = self.passedSeconds + 1 if self.passedSeconds == self.timeout: self.timer.Cancel() self.handler() else: self.timer.Restart()
class DSBlocker: def __init__(self, releaseHander): self.releaseHander = releaseHander self.timeout = -1 self.timer = DSTimer(1, self.timer_elapsed) self.initialize() def Block(self, timeout): if timeout > 0: self.timeout = timeout return self.block( ) # blocks the current thread until the condition is satisfied via the releaseHander # ---------------------------------------------------------------------------------------- handlers def timer_elapsed(self): if self.timeout > 0: self.timeout -= 1 self.timer.Restart() # ------------------------------ private methods def initialize(self): self.timer.Start() def block(self): while True: if (self.releaseHander != None and self.releaseHander() == True): self.timeout = -1 return 0 elif self.timeout == 0: self.timeout = -1 return 1
class DSProcess(): def __init__(self, id, isCoordinator): self.Id = id self.IsCoordinator = isCoordinator self.Data = None self.DSStatus = DSProcessStatus.Stopped self.DSSocket = None self.CurrentCoordinatorTransaction = None self.CurrentParticipantOperation = None self.CoordinatorTransactionLog = [] self.OperationTransactionLog = [] self.flipParticipantAcknowledge = False self.timeFailure = False self.timeFailureTimeout = 0 self.timer = DSTimer(1, self.timer_elapsed) def Initialize(self, data): self.Data = data def Run(self, startTimer=True): self.DSStatus = DSProcessStatus.Running port = portManager.GetANewPort() socketAddress = DSSocketAddress(port) self.DSSocket = DSSocket(socketAddress, self) self.DSSocket.Open() if startTimer: self.timer.Start() def Dispose(self): self.DSStatus = DSProcessStatus.Stopped self.DSSocket.Close() self.timer.Cancel() # ---------------------------------------------------------------------------------------- handlers def timer_elapsed(self): if self.timeFailure == True: self.timeFailureTimeout -= 1 if self.timeFailureTimeout <= 0: self.timeFailure = False self.timeFailureTimeout = 0 print('process ' + self.Id + ' is back from time failure') self.timer.Restart() # ---------------------------------------------------------------------------------------- Commands def PingCommandHandler(self, dsMessage): if self.DSStatus == DSProcessStatus.Stopped: return "I am stopped :( , my ID is: '" + str(self.Id) + "'" elif self.DSStatus == DSProcessStatus.Running: return "Hey... I'm running and my ID is: '" + str(self.Id) + "'" def GetDataCommandHandler(self, dsMessage): return "[" + ", ".join(self.Data) + "]" def ArbitraryFailureCommandHandler(self, dsMessage): self.toggleFlipParticipantAcknowledge() timeout = dsMessage.Argument dsTimeout = DSTimeout(timeout) dsTimeout.Run(self.toggleFlipParticipantAcknowledge) return str( timeout ) + ' seconds of arbitrary failure applied for the process ' + self.Id def TimeFailureCommandHandler(self, dsMessage): if dsMessage.Argument > 0: self.timeFailureTimeout = dsMessage.Argument self.timeFailure = True return 'timeFailure applied' # ------------- private methods def toggleFlipParticipantAcknowledge(self): self.flipParticipantAcknowledge = not self.flipParticipantAcknowledge if self.CurrentParticipantOperation != None: self.CurrentParticipantOperation.SetFlipParticipantAcknowledge( self.CurrentParticipantOperation) if self.flipParticipantAcknowledge == False: print('process ' + self.Id + ' is back from arbitrary failure') # ------------------------------------- coordinator-side command handlers def SetNewValueCommandHandler(self, dsMessage): return self.HandleOperation(dsMessage) def RollbackValuesCommandHandler(self, dsMessage): return self.HandleOperation(dsMessage) def VoteCommitCommandHandler(self, dsMessage): if self.CurrentCoordinatorTransaction != None and self.CurrentCoordinatorTransaction.Id == dsMessage.Argument: self.CurrentCoordinatorTransaction.HandleAcknowledge(True) def VoteAbortCommandHandler(self, dsMessage): if self.CurrentCoordinatorTransaction != None and self.CurrentCoordinatorTransaction.Id == dsMessage.Argument: self.CurrentCoordinatorTransaction.HandleAcknowledge(False) def PreCommitAcknowledgeCommandHandler(self, dsMessage): if self.CurrentCoordinatorTransaction != None and self.CurrentCoordinatorTransaction.Id == dsMessage.Argument: self.CurrentCoordinatorTransaction.HandlePreCommitAcknowledge() def SyncNewProcessCommandHandler(self, dsMessage): newProcess = dsMessage.Argument newProcess.Initialize(self.Data.copy()) # ------------- private methods def HandleOperation(self, dsMessage): self.CurrentCoordinatorTransaction = DSCoordinatorTransaction( self.onTransactionCommitHandler, self.onTransactionAbortHandler) (err, pid) = self.CurrentCoordinatorTransaction.Open() if err == 'timeout': return 'the operation aborted since the process with the id \'' + pid + '\' is not responding' (err, pid) = self.CurrentCoordinatorTransaction.Operate(dsMessage) if err == 'timeout': return 'the operation aborted since the process with the id \'' + pid + '\' is not responding' return ", ".join(self.Data) def onTransactionCommitHandler(self, operation): self.applyChangesInCoordinatorData(operation) self.onEndTransaction() def onTransactionAbortHandler(self): self.onEndTransaction() def onEndTransaction(self): self.CoordinatorTransactionLog.append( self.CurrentCoordinatorTransaction) def applyChangesInCoordinatorData(self, operation): if operation.Type == DSMessageType.SetNewValue: self.Data.append(operation.Argument) elif operation.Type == DSMessageType.RollbackValues: for x in range(int(operation.Argument)): self.Data.pop(len(self.Data) - 1) # ------------------------------------- participant-side command handlers def InitRequestCommandHandler(self, dsMessage): self.CurrentParticipantOperation = DSParticipantOperation( self, dsMessage.Argument, self.Data, self.flipParticipantAcknowledge, self.onOperationCommitHandler, self.onOperationAbortHandler) def VoteRequestCommandHandler(self, dsMessage): if self.CurrentParticipantOperation != None and self.CurrentParticipantOperation.CoordinatorTransactionId == dsMessage.Tag: self.CurrentParticipantOperation.Operate( dsMessage.Argument ) # dsMessage.Argument containing a dsMessage determining an operation def PreCommitCommandHandler(self, dsMessage): if self.CurrentParticipantOperation != None and self.CurrentParticipantOperation.CoordinatorTransactionId == dsMessage.Argument: self.CurrentParticipantOperation.PreCommit() def GlobalCommitCommandHandler(self, dsMessage): if self.CurrentParticipantOperation != None and self.CurrentParticipantOperation.CoordinatorTransactionId == dsMessage.Argument: self.CurrentParticipantOperation.Commit() def GlobalAbortCommandHandler(self, dsMessage): if self.CurrentParticipantOperation != None and self.CurrentParticipantOperation.CoordinatorTransactionId == dsMessage.Argument: self.CurrentParticipantOperation.Abort() def GetParticipantStateCommandHandler(self, dsMessage): operation = self.CurrentParticipantOperation if operation == None: coordinatorTransactionId = dsMessage.Argument for o in self.OperationTransactionLog: if o.CoordinatorTransactionId == coordinatorTransactionId: operation = o break return operation.State # ------------- private methods def onOperationCommitHandler(self): self.onEndOperation() def onOperationAbortHandler(self): self.onEndOperation() def onEndOperation(self): self.OperationTransactionLog.append(self.CurrentParticipantOperation)
def __init__(self, releaseHander): self.releaseHander = releaseHander self.timeout = -1 self.timer = DSTimer(1, self.timer_elapsed) self.initialize()
class BullyProcess(): def __init__(self, id, name, participationCounter, clock): self.Id = id self.CoordinatorProcessId = None self.Name = name self.ParticipationCounter = participationCounter self.DefaultClock = clock self.Clock = clock self.Status = BullyProcessStatus.Killed self.sharedData = None self.DSSocket = None self.timer = DSTimer(2, self.timer_elapsed) def Init(self, sharedData): self.sharedData = sharedData def Run(self, startTimer=True): self.Status = BullyProcessStatus.Run port = portManager.GetANewPort() socketAddress = DSSocketAddress(port) self.DSSocket = DSSocket(socketAddress, self) self.DSSocket.Open() if startTimer: self.timer.Start() def ResetTimer(self): self.timer.Restart() def ToString(self): inf = str(self.Id) + ", " + self.Name + "_" + str( self.getParticipationCounterAsString()) + ", " + self.Clock if self.isCoordinator(): inf += " (Coordinator)" if self.isSuspended(): inf += " (Suspended)" return inf def Dispose(self): self.DSSocket.Close() self.timer.Cancel() # ---------------------------------------------------------------------------------------- handlers def timer_elapsed(self): if BullyProcess.GetCoordinator(self.sharedData) != None: self.clockSynchronization() self.timer.Restart() def clockSynchronization(self): if not self.isCoordinator(): self.syncClock() # ---------------------------------------------------------------------------------------- Commands def PingCommandHandler(self, dsMessage): if self.Status == BullyProcessStatus.Killed: return "I am killed :( , my ID is: '" + str(self.Id) + "'" elif self.Status == BullyProcessStatus.Suspended: return "I am suspended unfortunately :( , my ID is: '" + str( self.Id) + "'" elif self.Status == BullyProcessStatus.Run: res = "Hey... I'm running and my ID is: '" + str(self.Id) + "'" if self.isCoordinator(): res += " , I'm also the **COORDINATOR**" return res def NudgeCommandHandler(self, dsMessage): return self.Id def StartElectionCommandHandler(self, dsMessage): self.updateParticipation() self.startElection() def NewCoordinatorCommandHandler(self, dsMessage): self.CoordinatorProcessId = int(dsMessage.Argument) def ListCommandHandler(self, dsMessage): desc = str(self.Id) + ", " + self.Name + "_" + str( self.getParticipationCounterAsString()) if self.Id == self.CoordinatorProcessId: desc += " (Coordinator)" desc += "\n" processes = list( filter(lambda x: x.Id > self.Id, self.sharedData.BullyProcesses)) processesLength = len(processes) if processesLength != 0: processes = BullyProcess.GetSortProcessList(processes) nextProc = processes[0] msg = DSMessage(DSMessageType.List) result = nextProc.DSSocket.SendMessage(msg) desc += result return desc def ClockCommandHandler(self, dsMessage): self.updateClock() desc = self.Name + "_" + str( self.getParticipationCounterAsString()) + ", " + self.Clock + "\n" processes = list( filter(lambda x: x.Id > self.Id, self.sharedData.BullyProcesses)) processesLength = len(processes) if processesLength != 0: processes = BullyProcess.GetSortProcessList(processes) nextProc = processes[0] msg = DSMessage(DSMessageType.Clock) result = nextProc.DSSocket.SendMessage(msg) desc += result return desc def SetTimeCommandHandler(self, dsMessage): self.Clock = dsMessage.Argument return "Clock is changed" def GetClockCommandHandler(self, dsMessage): return self.Clock def KillCommandHandler(self, dsMessage): result = None self.kill() if self.isCoordinator(): self.resetClocks() result = BullyProcess.StartElectionFromFirstProcess( self.sharedData) return result def ResetClockCommandHandler(self, dsMessage): self.Clock = self.DefaultClock def FreezeCommandHandler(self, dsMessage): amICoordinator = self.isCoordinator() self.Status = BullyProcessStatus.Suspended self.Dispose() if amICoordinator: BullyProcess.StartElectionFromFirstProcess(self.sharedData) return "The process freezed." def UnfreezeCommandHandler(self, dsMessage): coordinator = BullyProcess.GetCoordinator(self.sharedData) self.Status = BullyProcessStatus.Run self.timer.Restart() self.DSSocket.Open() if coordinator.Id < self.Id: self.Clock = self.DefaultClock self.startElection() else: self.updateClock() return "The process unfreezed." # --------------------------------------------------------------------------------- Private Methods def kill(self): self.Status = BullyProcessStatus.Killed self.sharedData.RemoveProcess(self.Id) self.Dispose() def syncClock(self): if BullyProcess.GetCoordinator(self.sharedData) != None: self.Clock = self.getCoordinatorClock() else: self.Clock = self.DefaultClock def getCoordinatorClock(self): process = BullyProcess.GetCoordinator(self.sharedData) return process.DSSocket.SendMessage(DSMessage(DSMessageType.GetClock)) def isCoordinator(self): return self.Id == self.CoordinatorProcessId and not self.isSuspended() def updateClock(self): if not self.isCoordinator() and not self.isSuspended(): self.syncClock() def resetClocks(self): for process in self.sharedData.BullyProcesses: process.DSSocket.SendMessage(DSMessage(DSMessageType.ResetClock)) def getNextProcessID(self): NextProcessId = -1 processes = list( filter(lambda x: x.Id > self.Id and not x.isSuspended(), self.sharedData.BullyProcesses)) processesLength = len(processes) if processesLength != 0: processIds = [] for index, process in enumerate(processes): msg = DSMessage(DSMessageType.Nudge) result = process.DSSocket.SendMessage(msg) if result: processId = int(result) if processId: processIds.append(processId) if len(processIds) != 0: processIds.sort() NextProcessId = processIds[0] return NextProcessId def notifyProcessesAboutNewCoordinator(self): self.CoordinatorProcessId = self.Id for index, process in enumerate(self.sharedData.BullyProcesses): if process.Id != self.Id: msg = DSMessage(DSMessageType.NewCoordinator, self.Id) process.DSSocket.SendMessage(msg) def isSuspended(self): return self.Status == BullyProcessStatus.Suspended def isRun(self): return self.Status == BullyProcessStatus.Run def startElection(self): nextProcessId = self.getNextProcessID() if nextProcessId == -1: self.notifyProcessesAboutNewCoordinator() else: processes = list( filter(lambda x: x.Id == nextProcessId, self.sharedData.BullyProcesses)) processes = BullyProcess.GetSortProcessList(processes) nextProc = processes[0] msg = DSMessage(DSMessageType.StartElection) nextProc.DSSocket.SendMessage(msg) def updateParticipation(self): if self.ParticipationCounter == None: self.ParticipationCounter = 0 else: self.ParticipationCounter = self.ParticipationCounter + 1 def getParticipationCounterAsString(self): counter = 0 if self.ParticipationCounter != None: counter = self.ParticipationCounter return str(counter) # --------------------------------------------------------------------------------- static Methods @staticmethod def StartElectionFromFirstProcess(sharedData): processes = list( filter(lambda x: not x.isSuspended(), sharedData.BullyProcesses)) processes = BullyProcess.GetSortProcessList(processes) if len(processes) == 0: return firstProc = processes[0] firstProc.DSSocket.SendMessage(DSMessage(DSMessageType.StartElection)) return firstProc.Id @staticmethod def GetCoordinator(sharedData): coordinatorProc = None for _, process in enumerate(sharedData.BullyProcesses): if process.CoordinatorProcessId == process.Id and not process.isSuspended( ): coordinatorProc = process break return coordinatorProc @staticmethod def GetSortProcessList(processes): return sorted(processes, key=lambda proc: proc.Id)
def __init__(self, timeout): self.timeout = timeout self.handler = None self.timer = DSTimer(1, self.timer_elapsed) self.passedSeconds = 0