// $Id: ServerThread.java,v 3.10 1998/12/14 20:18:03 smg9c Exp $
/////////////////////////////////////////////////////////////////////
//
// File: ServerThread.java
//
// Purpose: The ServerEngine class is responsible for manipulating the
// master map and sending it out to the clients.
//
// Authors: Steve Geist smg9c@cs.virginia.edu
//
// Modifications:
//
// 14-DEC-1998 smg Fixed "sum" propagation
//
// 11-DEC-1998 smg Removed annoying print messages about stable state and
// new map sent to all
//
// 09-DEC-1998 smg Fixed double send of final stats and send of tick 0 stats
//
// 09-DEC-1998 smg Added a final send of map and stats for each iteration
// in automatic mode
//
// 08-DEC-1998 smg Removed code for use with only one thread
//
// 04-DEC-1998 smg Changes made to run with auto and manual modes.
//
// 01-DEC-1998 smg Added automatic destabilization option
//
// 01-DEC-1998 smg Added file writes after each propagation
//
// 23-NOV-1998 smg Added GenerateInitialStats method and a call to it to
// send out stats at the beginning of the stabilization
// round
//
// 20-NOV-1998 smg Changed prop methods to use new GetDepends method
//
// 20-NOV-1998 smg Fixed prop method "sum" error
//
// 20-NOV-1998 smg Fixed -1 exception.
//
// 19-NOV-1998 smg Added Error method call to handle exceptions, fixed
// "all" prop method.
//
// 18-NOV 1998 smg Added check to see if node on other end of depend is
// unstable before trying to pass on instability
//
// 17-NOV-1998 smg Added instability propagation methods, added delay
// and propagation_method variables, added SendMap and
// SendStats methods, added statistics gathering
//
// 09-NOV-1998 smg Added comments and thread sleep command
//
// 06-NOV-1998 smg Initial creation
/////////////////////////////////////////////////////////////////////
import java.util.*;
import java.io.*;
public class ServerThread extends Thread {
private ServerRMI rmi; // RMI connection for sending map
private ServerEngine engine; // ServerEngine necessary for access to
// current map and number of clients
private Map map; // The current system Map
private long delay; // Delay in milliseconds between sending
// maps to all clients
private int propagation_method; // Indicates the propagation method to be
// used for unstable nodes
private Random generator; // Random number generator
private int num_clients; // Number of clients in the system
private Stats local_stats; // Local statistics
private Stats global_stats; // Global statistics
private int[] recoveries; // Recoveries made in each module this turn
private String map_file; // Name for saving the map to a file
private int map_file_count; // Int for appending to map file name
private int mode; // Indicates auto or manual mode
private int iterations; // Indicates how many iterations to run in
// auto mode
private final int automatic_mode = 0; // Auto mode
private final int manual_mode = 1; // Manual mode
///////////////////////////////////////////////////////////////////
//
// Method: ServerThread [constructor]
//
// Invoker: ServerEngine
//
// Purpose: Creates a new ServerThread generating the necessary values
// for private variables from the passed ServerEngine.
//
///////////////////////////////////////////////////////////////////
public ServerThread(ServerRMI r, ServerEngine se) {
rmi = r;
engine = se;
num_clients = se.GetNumClients();
Date d = new Date();
generator = new Random(d.getTime());
map = se.GetMap();
try {
local_stats = new Stats(num_clients);
global_stats = new Stats(num_clients);
}
catch (Exception e) {
engine.Error(e);
}
delay = se.GetDelay();
propagation_method = se.GetPropagationMethod();
recoveries = new int[num_clients];
for(int i=0;i<num_clients;i++) {
recoveries[i] = 0;
}
map_file = "smap";
map_file_count = 1;
mode = se.GetMode();
iterations = se.GetIterations();
}
///////////////////////////////////////////////////////////////////
//
// Method: run
//
// Invoker: ServerEngine (as start())
//
// Purpose: The run method sends out the modified map to all clients,
// stabilizes the map through a series of propagations and
// spontaneous node stabilizations, generates statistics, and
// sends out each of the intermediate maps and stats to all
// clients and the demon.
//
///////////////////////////////////////////////////////////////////
public void run() {
try {
map.SetFinal(false);
for(int j=0; j<iterations; j++) {
if(mode == automatic_mode) { // If mode is automatic
map.SetStable(false); // destabilize map
}
local_stats.Reset();
GenerateInitialStats();
while(true) {
if(map.IsStable()) {
SendMap();
SendLocalStats();
break;
}
SendMap();
SendLocalStats();
WriteMap();
// Pause to let all clients view new map
sleep(delay);
AttemptRecovery();
PropagateInstabilities();
}
}
// Send out stable map to all clients
map.SetFinal(true);
SendMap();
engine.SetThreadRunning(false);
rmi.RunEnded();
if(mode == automatic_mode) {
SendGlobalStats();
}
stop();
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: SendMap
//
// Invoker: (internal)
//
// Purpose: SendMap sends the current map to all clients and the demon.
//
///////////////////////////////////////////////////////////////////
private void SendMap() {
// Send out map to all clients
try {
for(int i=0; i<num_clients; i++) {
rmi.SendMapClient(map, i);
}
rmi.SendMapDemon(map);
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: SendLocalStats
//
// Invoker: (internal)
//
// Purpose: SendStats sends the current local stats to all clients and the
// demon.
//
///////////////////////////////////////////////////////////////////
private void SendLocalStats() {
// Send out stats to all clients
try {
for(int i=0; i<num_clients; i++) {
rmi.SendStatsClient(local_stats, i);
}
rmi.SendStatsDemon(local_stats);
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: SendGlobalStats
//
// Invoker: (internal)
//
// Purpose: SendStats sends the current global stats to all clients and the
// demon.
//
///////////////////////////////////////////////////////////////////
private void SendGlobalStats() {
// Send out stats to all clients
try {
for(int i=0; i<num_clients; i++) {
rmi.SendStatsClient(global_stats, i);
}
rmi.SendStatsDemon(global_stats);
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: WriteMap
//
// Invoker: (internal)
//
// Purpose: WriteMap writes the map out to a file.
//
///////////////////////////////////////////////////////////////////
private void WriteMap()
throws Exception {
File map_output = new File(map_file + map_file_count + ".txt");
DataOutputStream dos = new DataOutputStream(new FileOutputStream(map_output));
dos.writeBytes(map.ToSerial());
dos.close();
map_file_count++;
}
///////////////////////////////////////////////////////////////////
//
// Method: GenerateInitialStats
//
// Invoker: (internal)
//
// Purpose: GenerateInitialStats sets the unstable_nodes values to the
// correct numbers.
//
///////////////////////////////////////////////////////////////////
private void GenerateInitialStats() {
Module m;
Node n;
int num_nodes;
int num_unstable = 0;
try {
for(int i=0;i<num_clients;i++) {
m = map.GetModule(i);
num_unstable = 0;
num_nodes = m.GetNumNodes();
for(int j=0;j<num_nodes;j++) {
n = m.GetNode(j);
if(!n.IsStable()) {
num_unstable++;
}
}
local_stats.LogTick(i, 0, num_unstable, num_unstable);
if(mode == automatic_mode) {
global_stats.LogTick(i, 0, num_unstable, num_unstable);
}
}
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: AttemptRecovery
//
// Invoker: (internal)
//
// Purpose: AttemptRecovery takes each node in the map and make it stable
// with probability equal to the value of the recovery variable
// of that node.
//
///////////////////////////////////////////////////////////////////
private void AttemptRecovery() {
int num_nodes;
Module m;
Node n;
double recovery;
int num_recoveries;
ResetRecoveries();
try {
for(int i=0;i<num_clients;i++) {
num_recoveries = 0;
m = map.GetModule(i);
num_nodes = m.GetNumNodes();
for(int j=0;j<num_nodes;j++) {
n = m.GetNode(j);
if(!n.IsStable()) {
recovery = n.GetRecovery();
if(generator.nextDouble() < recovery) {
n.SetStable(true);
num_recoveries++;
}
}
}
recoveries[i] = num_recoveries;
}
}
catch (Exception e) {
engine.Error(e);
}
}
///////////////////////////////////////////////////////////////////
//
// Method: ResetRecoveries
//
// Invoker: (internal)
//
// Purpose: ResetRecoveries resets the recoveries array to all 0's.
//
///////////////////////////////////////////////////////////////////
private void ResetRecoveries() {
for(int i=0;i<recoveries.length;i++) {
recoveries[i] = 0;
}
}
///////////////////////////////////////////////////////////////////
//
// Method: PropagateInstabilities
//
// Invoker: (internal)
//
// Purpose: PropagateInstabilities uses to given propagation method to
// call the correct propagation function.
//
///////////////////////////////////////////////////////////////////
private void PropagateInstabilities()
throws Exception {
switch(propagation_method) {
case 1:
PropInstabilitiesAny();
break;
case 2:
PropInstabilitiesSum();
break;
case 3:
PropInstabilitiesAll();
break;
default:
engine.PrintMessage("Invalid propagation method variable");
break;
}
}
///////////////////////////////////////////////////////////////////
//
// Method: PropInstabilitiesAny
//
// Invoker: (internal)
//
// Purpose: PropInstabilitiesAny propagates instabilities throughout
// the map. A node becomes unstable if any of its dependencies
// fire.
//
///////////////////////////////////////////////////////////////////
private void PropInstabilitiesAny()
throws Exception {
Map old_map = new Map(map);
Module m;
Module current_module;
Node n;
Node current_node;
int num_nodes;
int num_destabilizations;
int num_unstable;
Depend[] dependencies;
double prob = 0.0;
// This loop propagates instabilities
for(int mod_num=0;mod_num<num_clients;mod_num++) {
m = map.GetModule(mod_num);
num_nodes = m.GetNumNodes();
num_destabilizations = 0;
num_unstable = 0;
for(int node_num=0;node_num<num_nodes;node_num++) {
n = m.GetNode(node_num);
// Only try to destabilize if the node is currently stable
if(n.IsStable()) {
// Get dependencies for this node
dependencies = map.GetDepends(mod_num, node_num, true);
// Check each dependency
for(int j=0;j<dependencies.length;j++) {
current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
if(!current_node.IsStable()) {
prob = dependencies[j].GetProbability();
// If the dependency fires...
if(generator.nextDouble() < prob) {
// set node unstable
n.SetStable(false);
num_destabilizations++;
// End for loops
j = dependencies.length;
}
}
}
}
if(!n.IsStable()) {
num_unstable++;
}
}
local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
num_unstable);
if(mode == automatic_mode) {
global_stats.LogTick(mod_num, recoveries[mod_num],
num_destabilizations, num_unstable);
}
}
}
///////////////////////////////////////////////////////////////////
//
// Method: PropInstabilitiesSum
//
// Invoker: (internal)
//
// Purpose: PropInstabilitiesSum propagates instabilities throughout the
// map. A node becomes unstable if the inverse of the product
// of the inverses of the dependencies fire.
//
///////////////////////////////////////////////////////////////////
private void PropInstabilitiesSum()
throws Exception {
Map old_map = new Map(map);
Module m;
Module current_module;
Node n;
Node current_node;
int num_nodes;
int num_destabilizations;
int num_unstable;
Depend[] dependencies;
double prob = 0.0;
double inverse = 0.0;
double node_prob = 0.0;
// This loop propagates instabilities
for(int mod_num=0;mod_num<num_clients;mod_num++) {
m = map.GetModule(mod_num);
num_nodes = m.GetNumNodes();
num_destabilizations = 0;
num_unstable = 0;
for(int node_num=0;node_num<num_nodes;node_num++) {
n = m.GetNode(node_num);
prob = 0.0;
// Only try to destabilize if the node is currently stable
if(n.IsStable()) {
// Get dependencies for this node
dependencies = map.GetDepends(mod_num, node_num, true);
// Check each dependency
for(int j=0;j<dependencies.length;j++) {
current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
if(!current_node.IsStable()) {
node_prob = dependencies[j].GetProbability();
inverse = 1 - node_prob;
// Multiply this inverse by current product
if(prob == 0.0) {
prob = inverse;
}
else {
prob = prob * inverse;
}
}
}
// Take the inverse of the product of the inverses
if(prob != 0.0) {
prob = 1 - prob;
}
// If the dependency fires...
if(generator.nextDouble() < prob) {
// set node unstable
n.SetStable(false);
num_destabilizations++;
}
}
if(!n.IsStable()) {
num_unstable++;
}
}
local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
num_unstable);
if(mode == automatic_mode) {
global_stats.LogTick(mod_num, recoveries[mod_num],
num_destabilizations, num_unstable);
}
}
}
///////////////////////////////////////////////////////////////////
//
// Method: PropInstabilitiesAny
//
// Invoker: (internal)
//
// Purpose: PropInstabilitiesAll propagates instabilities throughout the
// map. A node becomes unstable only if all of its dependencies
// fire.
//
///////////////////////////////////////////////////////////////////
private void PropInstabilitiesAll()
throws Exception {
Map old_map = new Map(map);
Module m;
Module current_module;
Node n;
Node current_node;
int num_nodes;
int num_destabilizations;
int num_unstable;
Depend[] dependencies;
double prob = 0.0;
boolean unstable_depend; // Indicates whether this node has an
// unstable dependecy
boolean all_fired; // Indicates whether all dependencies
// fired for the current node
// This loop propagates instabilities
for(int mod_num=0;mod_num<num_clients;mod_num++) {
m = map.GetModule(mod_num);
num_nodes = m.GetNumNodes();
num_destabilizations = 0;
num_unstable = 0;
for(int node_num=0;node_num<num_nodes;node_num++) {
n = m.GetNode(node_num);
// Only try to destabilize if the node is currently stable
if(n.IsStable()) {
unstable_depend = false;
all_fired = true;
// Get dependencies for this node
dependencies = map.GetDepends(mod_num, node_num, true);
// Check each dependency
for(int j=0;j<dependencies.length;j++) {
current_module = old_map.GetModule(dependencies[j].GetSourceModuleID());
current_node = current_module.GetNode(dependencies[j].GetSourceNodeID());
if(!current_node.IsStable()) {
unstable_depend = true;
prob = dependencies[j].GetProbability();
// If the dependency doesn't fire...
if(generator.nextDouble() > prob) {
all_fired = false;
// Terminate for loops
j = dependencies.length;
}
}
}
// Check to see if node should be destabilized
if(unstable_depend && all_fired) {
n.SetStable(false);
num_destabilizations++;
}
}
if(!n.IsStable()) {
num_unstable++;
}
}
local_stats.LogTick(mod_num, recoveries[mod_num], num_destabilizations,
num_unstable);
if(mode == automatic_mode) {
global_stats.LogTick(mod_num, recoveries[mod_num],
num_destabilizations, num_unstable);
}
}
}
}