int add_transaction_metadata(int tx_id, int local, int tx_run_number, SERVER_lp_state_type * pointer) {
	if (!get_transaction_metadata(tx_id, pointer)) {
		int hash_table_bucket = active_transaction_table_hash_function(tx_id);
		transaction_metadata * new_transaction_metadata = (transaction_metadata *) malloc(sizeof(transaction_metadata));
		new_transaction_metadata->tx_id = tx_id;
		new_transaction_metadata->local = local;
		new_transaction_metadata->executed_operations = 0;
		new_transaction_metadata->current_tx_run_number = tx_run_number;
		new_transaction_metadata->is_blocked = 0;
		new_transaction_metadata->expected_response_counter = 0;
		new_transaction_metadata->next = NULL;
		new_transaction_metadata->write_set = NULL;
		new_transaction_metadata->read_set = NULL;
		if (pointer->cc_metadata->active_transaction[hash_table_bucket] == NULL) {
			pointer->cc_metadata->active_transaction[hash_table_bucket] = new_transaction_metadata;
			return 1;
		} else {
			transaction_metadata * tmd = (transaction_metadata *) pointer->cc_metadata->active_transaction[hash_table_bucket];
			while (tmd->next != NULL) {
				if (tmd->tx_id == tx_id) {
					return -1;
				}
				tmd = tmd->next;

			}
			tmd->next = new_transaction_metadata;
			return 1;
		}
	}
	return 1;
}
static int add_data_to_write_set(int tx_id, int client_id, SERVER_lp_state_type * pointer, int object_key_id) {
	data_set_entry * entry = (data_set_entry *) malloc(sizeof(data_set_entry));
	entry->object_key_id = object_key_id;
	entry->next = NULL;
	transaction_metadata * transaction = get_transaction_metadata(tx_id, pointer);
	if (transaction == NULL) {
		printf("ERROR: no transaction found with id %d (from client id %d)\n", tx_id, client_id);
		return -1;
	}
	if (transaction->write_set == NULL) {
		transaction->write_set = entry;
		return 1;
	}
	data_set_entry * current_entry = transaction->write_set;
	while (current_entry->next != NULL) {
		if (current_entry->object_key_id == object_key_id) {
			return 0;
		}
		current_entry = current_entry->next;
	}

	current_entry->next = entry;

	return 1;
}
//send prepare messages to other servers
void send_final_commit_messages(state_type *state, event_content_type * event_content, time_type now) {
    int s;
    // Get the server configuration from simulation state
    SERVER_lp_state_type *pointer = &state->type.server_state;
    transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
    if (transaction == NULL) {
        printf("ERROR: no transaction fouded with id %d (from cliendt %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
        exit(-1);
    }
    for (s = state->num_clients; s < state->num_clients + state->num_servers; s++) {
        int a_server_founded = 0;
        data_set_entry *entry = transaction->write_set;
        while (entry != NULL && !a_server_founded) {
            if (pointer->configuration.concurrency_control_type == PRIMARY_OWNER_CTL_2PL) {
                if (is_primary(entry->object_key_id, s, state->num_servers, state->num_clients))
                    a_server_founded = 1;
            } else {
                if (is_owner(entry->object_key_id, s, state->num_servers, state->num_clients, state->object_replication_degree))
                    a_server_founded = 1;
            }
            entry = entry->next;
        }
        if (s != event_content->applicative_content.server_id && a_server_founded) {
            event_content_type new_event_content;
            memcpy(&new_event_content, event_content, sizeof(event_content_type));
            new_event_content.applicative_content.op_type = TX_DISTRIBUTED_FINAL_COMMIT;
            new_event_content.destination_object_id = s;
            new_event_content.applicative_content.write_set = transaction->write_set;
            transaction->expected_prepare_response_counter++;
            server_send_final_commit_message(state, &new_event_content, now);
            if (pointer->configuration.server_verbose)
                printf("S%d - function Server_ProcessEvent: TX_DISTRIBUTED_FINAL_COMMIT sent at time %f to server %i\n", event_content->applicative_content.server_id, now, s);
        }
    }
}
//send final abort messages to other servers
void send_final_abort_messages(state_type *state, event_content_type * event_content, time_type now) {
    int s;
    // Get the server configuration from simulation state
    SERVER_lp_state_type *pointer = &state->type.server_state;
    transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
    if (transaction == NULL) {
        printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
        exit(-1);
    }
    for (s = state->num_clients; s < state->num_clients + state->num_servers; s++) {
        // send a message to other servers containing at least an entry of the transaction write set
        int an_entry_founded = 0;
        data_set_entry *entry = transaction->write_set;
        while (entry != NULL && !an_entry_founded) {
            if (pointer->configuration.concurrency_control_type == PRIMARY_OWNER_CTL_2PL) {
                if (is_primary(entry->object_key_id, s, state->num_servers, state->num_clients))
                    an_entry_founded = 1;
            } else {
                if (is_owner(entry->object_key_id, s, state->num_servers, state->num_clients, state->object_replication_degree))
                    an_entry_founded = 1;
            }
            entry = entry->next;
        }
        if (s != event_content->applicative_content.server_id && an_entry_founded) {
            event_content->applicative_content.op_type = TX_REMOTE_ABORT;
            event_content->destination_object_id = s;
            event_content->applicative_content.write_set = transaction->write_set;
            transaction->expected_prepare_response_counter++;
            server_send_message(state, event_content, now);
            if (pointer->configuration.server_verbose)
                printf("S%d - function Server_ProcessEvent: TX_REMOTE_ABORT sent at time %f to server %i\n", event_content->applicative_content.server_id, now, s);
        }
    }
}
static void abort_local_tx(event_content_type * event_content, state_type *state, double now) {

	SERVER_lp_state_type *pointer = &state->type.server_state;
	//Remove all events of transaction
	remove_event_of_tx(pointer->cc_metadata->event_queue, event_content->applicative_content.tx_id);
	transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
	if (transaction == NULL) {
		printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
		exit(-1);
	}
	remove_tx_locks(transaction->tx_id, transaction->write_set, state, now);
}
int acquire_a_local_lock(state_type *state, time_type timeout, int timeout_event_type, event_content_type *event_content, double now) {
	SERVER_lp_state_type *pointer = &state->type.server_state;
	if (pointer->configuration.cc_verbose)
		printf("cc%d - oggetto %d per la transizione %i da lockare\n", pointer->server_id, event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id);
	//check lock...
	if (pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] == -1) {
		//not locked
		pointer->cc_metadata->lock_retry_num = 0;
		//acquire lock
		pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] = event_content->applicative_content.tx_id;
		if (pointer->configuration.cc_verbose)
			printf("cc%d - oggetto %d per la transizione %i lockato al tempo %f \n", pointer->server_id, event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id,
					now);
		return 1;
	} else if (pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] == event_content->applicative_content.tx_id) {
		// already locked by me
		return 1;
	} else {
		//already locked by another transaction
		pointer->cc_metadata->lock_retry_num++;
		//check deadlocks (if enabled)
		if (pointer->configuration.deadlock_detection_enabled && check_deadlock(event_content, pointer)) {
			return -1;
		}

		//add the timeout event
		event_content_type new_event_content;
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		ScheduleNewEvent(pointer->server_id, now + timeout, timeout_event_type, &new_event_content, sizeof(event_content_type));

		//enqueue event
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.object_key_id = event_content->applicative_content.object_key_id;
		enqueue_event(pointer, &new_event_content);

		transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction == NULL) {
			if (pointer->configuration.cc_verbose) {
				printf("cc%d - la tx %i non è locale\n", pointer->server_id, event_content->applicative_content.tx_id);
				printf("cc%d - prepare of tx %i added in the waiting event queue %f due to a lock on object :%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now,
						event_content->applicative_content.object_key_id, new_event_content.applicative_content.tx_id);
			}
			return 0;
		} else {
			transaction->is_blocked = 1;
			if (pointer->configuration.cc_verbose)
				printf("cc%d - tx %i is waiting at time %f due to a lock lock on object :%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now,
						event_content->applicative_content.object_key_id, new_event_content.applicative_content.tx_id);
			return 0;
		}
	}
}
static void reschedule_event(state_type *state, double now, int object_key_id) {

	SERVER_lp_state_type *pointer = &state->type.server_state;
	event_content_type *event_content = NULL;

	do {
		event_content = get_next_event_waiting_for_object_key(state, CC_QUEUE, object_key_id);
		if (event_content != NULL) {
			transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			if (transaction != NULL)
				transaction->is_blocked = 0;
			ScheduleNewEvent(pointer->server_id, now, DELIVER_MESSAGE, event_content, sizeof(event_content_type));
			if (pointer->configuration.cc_verbose)
				printf("cc - Funzione reschedule_event: reinviato evento %d (op %d della txn %d) a server %d al tempo %f\n", event_content->applicative_content.op_type,
						event_content->applicative_content.op_number, event_content->applicative_content.tx_id, event_content->applicative_content.server_id, now);
			remove_event_of_tx(pointer->cc_metadata->event_queue, event_content->applicative_content.tx_id);
		}
	} while (event_content != NULL);
}
static int add_data_to_read_set(int tx_id, int client_id, SERVER_lp_state_type * pointer, int object_key_id) {
	data_set_entry * entry = (data_set_entry *) malloc(sizeof(data_set_entry));
	entry->object_key_id = object_key_id;
	entry->next = NULL;
	transaction_metadata * transaction = get_transaction_metadata(tx_id, pointer);
	if (transaction == NULL) {
		printf("ERRORE (add_data_to_read_set):  Nessun active transaction con id %d del client %d presente\n", tx_id, client_id);
		return -1;
	}
	if (transaction->read_set == NULL) {
		transaction->read_set = entry;
		return 1;
	}
	data_set_entry * current_entry = transaction->read_set;
	while (current_entry->next != NULL) {
		if (current_entry->object_key_id == object_key_id) {
			return 0;
		}
		current_entry = current_entry->next;
	}
	current_entry->next = entry;

	return 1;
}
//main concurrency control function: return 0 if the event is enqueued, 1 if the accessing object is locked, -1 if transaction must be aborted
int CC_control(event_content_type * event_content, state_type *state, time_type now) {
	SERVER_lp_state_type *pointer = &state->type.server_state;
	if (pointer == NULL || event_content == NULL)
		return -1;
	switch (event_content->applicative_content.op_type) {

	case TX_BEGIN:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_BEGIN\n", pointer->server_id);
		return 1;
		break;

	case TX_GET:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_GET per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);

		int return_from_add_data_to_read_set = add_data_to_read_set(event_content->applicative_content.tx_id, event_content->applicative_content.client_id, pointer,
				event_content->applicative_content.object_key_id);
		if (return_from_add_data_to_read_set == -1) {
			abort_local_tx(event_content, state, now);
			if (pointer->configuration.cc_verbose)
				printf("\tcc%d - ERROR while adding object %i in the read-set of transaction %d from client %d\n", pointer->server_id,
						event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
			return -1;
		} else {
			if (return_from_add_data_to_read_set == 0 && pointer->configuration.cc_verbose)
				printf("\tcc%d - object %d is already in the read-set of transaction %d from client %d\n", pointer->server_id, event_content->applicative_content.object_key_id,
						event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
			else if (pointer->configuration.cc_verbose)
				printf("\tcc%d - object %d added to the read-set of transaction tx %d\n", pointer->server_id, event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id);
		}
		return 1;
		break;

	case TX_PUT:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d -  TX_PUT per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);

		//add data to write-set
		add_data_to_write_set(event_content->applicative_content.tx_id, event_content->applicative_content.client_id, pointer, event_content->applicative_content.object_key_id);
		if (pointer->configuration.concurrency_control_type == ETL_2PL
				&& is_owner(event_content->applicative_content.object_key_id, pointer->server_id, state->num_servers, state->num_clients, state->object_replication_degree)) {
			return acquire_a_local_lock(state, pointer->configuration.locking_timeout, TX_LOCAL_TIMEOUT, event_content, now);
		}
		return 1;
		break;

	case TX_PREPARE:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_PREPARE per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		data_set_entry *dataset = event_content->applicative_content.write_set;
		int result = acquire_local_locks(state, dataset, pointer->configuration.locking_timeout, TX_PREPARE_TIMEOUT, event_content, now);
		if (result == -1)
			abort_local_tx(event_content, state, now);
		return result;
		break;

	case TX_COMMIT:

		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_COMMIT per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		if (pointer->configuration.concurrency_control_type == CTL_2PL || pointer->configuration.concurrency_control_type == PRIMARY_OWNER_CTL_2PL) {
			transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			if (transaction == NULL) {
				printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
				exit(-1);
			}
			data_set_entry *dataset = transaction->write_set;
			int result = acquire_local_locks(state, dataset, pointer->configuration.locking_timeout, TX_LOCAL_TIMEOUT, event_content, now);
			if (result == -1)
				abort_local_tx(event_content, state, now);
			return result;
		} else
			return 1;
		break;

	case TX_FINAL_LOCAL_COMMIT:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_FINAL_LOCAL_COMMIT per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction == NULL) {
			printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
			exit(-1);
		}
		dataset = transaction->write_set;
		remove_tx_locks(event_content->applicative_content.tx_id, dataset, state, now);
		//} else
		return 1;
		break;

	case TX_FINAL_REMOTE_COMMIT:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_FINAL_REMOTE_COMMIT per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		remove_tx_locks(event_content->applicative_content.tx_id, event_content->applicative_content.write_set, state, now);
		return 1;
		break;

	case TX_LOCAL_ABORT:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_ABORT  per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		abort_local_tx(event_content, state, now);
		return -1;
		break;

	case TX_REMOTE_ABORT:
		if (pointer->configuration.cc_verbose)
			printf("\tcc%d - TX_ABORT  per tx %i\n", pointer->server_id, event_content->applicative_content.tx_id);
		abort_remote_tx(event_content, state, now);
		return -1;
		break;

	default:
		printf("ERRORE: evento %i non catturato\n", event_content->applicative_content.op_type);
		break;
	}

	printf("ERROR: case not managed\n");
	exit(-1);
}
int acquire_local_locks(state_type *state, data_set_entry *data_set, time_type timeout, int timeout_event_type, event_content_type *event_content, double now) {
	SERVER_lp_state_type *pointer = &state->type.server_state;
	data_set_entry *entry = data_set;
	if (entry == NULL) {
		if (pointer->configuration.cc_verbose)
			printf("cc%d -  write set of transaction %d is empty\n", pointer->server_id, event_content->applicative_content.tx_id);
		return 1;
	}
	while (entry != NULL) {
		int need_to_lock = 0;
		if (pointer->configuration.concurrency_control_type == ETL_2PL || pointer->configuration.concurrency_control_type == CTL_2PL)
			need_to_lock = is_owner(entry->object_key_id, pointer->server_id, state->num_servers, state->num_clients, state->object_replication_degree);
		else if (pointer->configuration.concurrency_control_type == PRIMARY_OWNER_CTL_2PL)
			need_to_lock = is_primary(entry->object_key_id, pointer->server_id, state->num_servers, state->num_clients);
		if (need_to_lock) {
			if (pointer->configuration.cc_verbose)
				printf("cc%d - object %d for transaction %i to be locked\n", pointer->server_id, entry->object_key_id, event_content->applicative_content.tx_id);
			//check lock...
			if (pointer->cc_metadata->locks[entry->object_key_id] == -1) {
				//not locked
				pointer->cc_metadata->lock_retry_num = 0;
				//acquire lock
				pointer->cc_metadata->locks[entry->object_key_id] = event_content->applicative_content.tx_id;
				if (pointer->configuration.cc_verbose)
					printf("cc%d - pbject %d  for transaction  %i locked at time %f \n", pointer->server_id, entry->object_key_id, event_content->applicative_content.tx_id, now);
			} else if (pointer->cc_metadata->locks[entry->object_key_id] == event_content->applicative_content.tx_id) {
				// already locked by me
				// go to the next entry
			} else {
				//already locked by another transaction
				pointer->cc_metadata->lock_retry_num++;
				//check deadlock (if enabled)
				if (pointer->configuration.deadlock_detection_enabled && check_deadlock(event_content, pointer)) {
					return -1;
				}
				//add the timeout event
				event_content_type new_event_content;
				memcpy(&new_event_content, event_content, sizeof(event_content_type));
				ScheduleNewEvent(pointer->server_id, now + timeout, timeout_event_type, &new_event_content, sizeof(event_content_type));

				//enqueue transaction
				memcpy(&new_event_content, event_content, sizeof(event_content_type));
				new_event_content.applicative_content.object_key_id = entry->object_key_id;
				enqueue_event(pointer, &new_event_content);

				transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
				if (transaction == NULL) {
					if (pointer->configuration.cc_verbose) {
						printf("cc%d - transaction %i is not local\n", pointer->server_id, event_content->applicative_content.tx_id);
						printf("cc%d - prepare of tx %i added in the waiting event queue %f due to a lock on object %d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now,
								entry->object_key_id, new_event_content.applicative_content.tx_id);
					}
					return 0;
				} else {
					transaction->is_blocked = 1;
					if (pointer->configuration.cc_verbose)
						printf("cc%d - transaction %i is waiting at time %f due to a lock on object%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now, entry->object_key_id,
								new_event_content.applicative_content.tx_id);
					return 0;
				}
			}
		}
		entry = entry->next;
	}
	return 1;
}
//function per il processamento degli eventi da parte del server
void process_message(int me, time_type now, event_content_type * event_content, state_type *state) {

	// Get the server configuration from simulation state
	SERVER_lp_state_type *pointer = &state->type.server_state;
	transaction_metadata * transaction;

	// check operation type
	switch (event_content->applicative_content.op_type) {

	case TX_BEGIN:

		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_BEGIN  at time %f for transaction %d run number %i\n", me, now, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number);
		//add transaction metadata to active_transaction list
		int ret = add_transaction_metadata(event_content->applicative_content.tx_id, 1, event_content->applicative_content.tx_run_number, state);
		int cc_response = CC_control(event_content, state, now);
		if (cc_response == 1) {
			event_content->applicative_content.op_type = CPU_TX_BEGIN;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_BEGIN added to CPU at time %f\n", me, now);
		} else if (cc_response == -1) {
			event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
		}
	 	transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		break;

	case TX_GET: //get received from a client

		if (is_in_write_set(event_content->applicative_content.tx_id, event_content->applicative_content.object_key_id, state)) {
			event_content->applicative_content.op_type = CPU_TX_LOCAL_GET;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_LOCAL_TX_GET added to CPU at time %f\n", me, now);
			break;
		}

		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_GET  at time %f\n", me, now);
		//am I the owner of the key?
		if (is_owner(event_content->applicative_content.object_key_id, me, state->num_servers, state->num_clients, state->object_replication_degree)) {
			//..yes, call the concurrency control
			int cc_response = CC_control(event_content, state, now);
			if (cc_response == 1) {
				//operation allowed
				event_content->applicative_content.op_type = CPU_TX_LOCAL_GET;
				add_processing_request(pointer, event_content, now);
				if (pointer->configuration.server_verbose)
					printf("S%d - CPU_LOCAL_TX_GET added to CPU at time %f\n", me, now);
			} else if (cc_response == -1) {
				//operation not allowed, transaction has to be aborted
				if (pointer->configuration.server_verbose)
					printf("S%d - Server_ProcessEvent: operation aborted by the concurrency control at time %f\n", me, now);
				event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
				remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
				add_processing_request(pointer, event_content, now);
				if (pointer->configuration.server_verbose)
					printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
			}
		} else {
		 	transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			//..no (I'not the owner), TX_GET has to be sent to remote server(s)
			event_content->applicative_content.op_type = CPU_TX_SEND_REMOTE_GET;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_SEND_REMOTE_TX_GET added to CPU at time %f\n", me, now);
		}
		break;

	case TX_REMOTE_GET: //get received from a server
		if (pointer->configuration.server_verbose)
			printf("S%d - Server_ProcessEvent: TX_REMOTE_GET received from server %i for operation %i of transaction %i at time %f\n", me, pointer->server_id,
					event_content->applicative_content.op_number, event_content->applicative_content.tx_id, now);
		if (event_content->applicative_content.tx_id==9 && event_content->applicative_content.op_number==6)
			fflush(stdout);
		cc_response = CC_control(event_content, state, now);
		if (cc_response == 1) {
			event_content->applicative_content.op_type = CPU_TX_LOCAL_GET_FROM_REMOTE;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_LOCAL_TX_GET_FROM_REMOTE added to CPU at time %f\n", me, now);
		}
		break;

	case TX_REMOTE_GET_RETURN: //get response received from a server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_REMOTE_GET_RETURN from server %i for operation %i tx %i run number %i at time %f\n", me, event_content->origin_object_id,
					event_content->applicative_content.op_number, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		if (event_content->applicative_content.tx_id == 9 && event_content->applicative_content.op_number == 6)
			fflush(stdout);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction!=NULL && event_content->applicative_content.op_number - 1 == transaction->executed_operations && transaction->current_tx_run_number == event_content->applicative_content.tx_run_number) {
			transaction->executed_operations++;
			int cc_response = CC_control(event_content, state, now);
			if (cc_response == 1) {
				event_content->applicative_content.op_type = CPU_TX_REMOTE_GET_RETURN;
				add_processing_request(pointer, event_content, now);
				if (pointer->configuration.server_verbose)
					printf("S%d - CPU_REMOTE_TX_GET_RETURN added to CPU at time %f\n", me, now);
			} else if (cc_response == -1) {
				event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
				remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
				add_processing_request(pointer, event_content, now);
				if (pointer->configuration.server_verbose)
					printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
			}
		}
		break;

	case TX_PUT: //put received from a client
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_PUT (txn %i, op: %i)  at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.op_number,
					now);
		//call the concurrency control
		cc_response = CC_control(event_content, state, now);
		if (cc_response == 1) {
			//operation allowed
			event_content->applicative_content.op_type = CPU_TX_LOCAL_PUT;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_LOCAL_TX_PUT added to CPU at time %f\n", me, now);
		} else if (cc_response == -1) {
			//operation not not allowed, transaction has to be aborted
			event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
		}
		break;

	case TX_UPDATE: //data update update received from a primary owner server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received UPDATE  at time %f \n", me, now);
		event_content->applicative_content.op_type = CPU_TX_UPDATE;
		add_processing_request(pointer, event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - CPU_UPDATE added to CPU at time %f\n", me, now);
		break;

	case TX_COMMIT: // commit request received from a client
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_COMMIT for transaction %i run number %i  at time %f \n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		cc_response = CC_control(event_content, state, now);
		if (cc_response == 1) {
			//operation allowed
			transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			if (transaction == NULL ) {
				printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
				exit(-1);
			}

			event_content->applicative_content.op_type = CPU_PREPARE;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_PREPARE for transaction %i added to CPU at time %f\n", me, event_content->applicative_content.tx_id, now);

		} else if (cc_response == -1) {
			////operation not allowed, transaction has to be aborted
			if (pointer->configuration.server_verbose)
				printf("S%d - function Server_ProcessEvent: operazione abortita dal Controllore di Concorrenza at time %f\n", me, now);
			event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
		}
		break;

	case TX_PREPARE: //prepare request received from a coordinator server
		ret = add_transaction_metadata(event_content->applicative_content.tx_id, 0, event_content->applicative_content.tx_run_number, state);
		if (ret == -1 && pointer->configuration.cc_verbose)
			printf("[cc%d] Error while adding transaction %i to transaction metadata list\n", pointer->server_id, event_content->applicative_content.tx_id);
		if (pointer->configuration.server_verbose)
			printf(" S%d - function Server_ProcessEvent: received TX_PREPARE for transaction %i run number %i at time %f \n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		int CC_response = CC_control(event_content, state, now);
		if (CC_response == 1) {
			event_content->applicative_content.op_type = CPU_TX_LOCAL_PREPARE_SUCCESSED;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_PREPARE_SUCCESSED added to CPU at time %f\n", me, now);
		} else if (CC_response == -1) {
			event_content->applicative_content.op_type = CPU_TX_LOCAL_PREPARE_FAILED;
			add_processing_request(pointer, event_content, now);
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_PREPARE_FAILED added to CPU at time %f\n", me, now);
		}
		break;

	case TX_PREPARE_SUCCEEDED: // received from a remote participant server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_PREPARE_SUCCEEDED for transaction %i run number %i at time %f \n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		// decrement expected response counter
		if (transaction != NULL && transaction->current_tx_run_number == event_content->applicative_content.tx_run_number) {
			transaction->expected_prepare_response_counter--;
			CC_control(event_content, state, now);
			if (transaction->expected_prepare_response_counter == 0) {
				//all expected prepare messages have been succesfully received, transaction can commit
				event_content->applicative_content.op_type = CPU_TX_DISTRIBUTED_FINAL_COMMIT;
				add_processing_request(pointer, event_content, now);
				if (pointer->configuration.server_verbose)
					printf("S%d - CPU_DISTRIBUTED_FINAL_TX_COMMIT to cpu of transaction %i at time %f\n", me, event_content->applicative_content.tx_id, now);
			}
		}
		break;

	case TX_PREPARE_FAILED: // received from a remote participant server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_PREPARE_FAILED for transaction %i at time %f\n", me, event_content->applicative_content.tx_id, now);
		event_content->applicative_content.op_type = CPU_TX_PREPARE_FAILED;
		add_processing_request(pointer, event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - CPU_TX_PREPARE_FAILED added to CPU at time %f\n", me, now);
		break;

	case TX_DISTRIBUTED_FINAL_COMMIT: //received from a remote coordinator server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_DISTRIBUTED_FINAL_COMMIT for transaction %i at time %f \n", me, event_content->applicative_content.tx_id, now);
		event_content->applicative_content.op_type = CPU_TX_LOCAL_FINAL_COMMIT;
		add_processing_request(pointer, event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - CPU_LOCAL_TX_FINAL_COMMIT for transaction %i added to CPU at time %f\n", me, event_content->applicative_content.tx_id, now);
		break;

	case TX_REMOTE_ABORT: // received from a remote coordinator server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_REMOTE_ABORT at time %f for transaction %d\n", me, now, event_content->applicative_content.tx_id);
		event_content->applicative_content.op_type = CPU_TX_REMOTE_ABORT;
		add_processing_request(pointer, event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - CPU_TX_REMOTE_ABORT added to CPU at time %f\n", me, now);
		break;

	default:
		printf("ERRORE: event type %i not managed\n", event_content->applicative_content.op_type);
		break;
	}
}
void SERVER_ProcessEvent(unsigned int me, time_type now, int event_type, event_content_type *event_content, unsigned int size, state_type *state) {

	// get the server configuration from simulation state
	SERVER_lp_state_type *pointer = &state->type.server_state;
	transaction_metadata *transaction;

	switch (event_type) {

	// Configuration has just been stored
	case INIT:
		if (pointer->configuration.server_verbose) {
			printf("S%d - function Server_ProcessEvent: received SHARE_CONF\n", me);
		}
		if (me>state->num_clients+state->num_servers)
			break;
		pointer->server_id = me;
		pointer->stored_values = 0;
		pointer->last_net_iterarrival_tx_time=0;
		pointer->average_net_interarrival_tx_time=0;
		concurrency_control_init(state);
		cpu_init(pointer);

		break;

	case CPU_TX_LOCAL_GET: //return from cpu in case of local get
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received READ_LOCAL_VALUE_RET_CPU  at time %f\n", me, now);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction == NULL) {
			printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
			exit(-1);
		}
		transaction->executed_operations++;
		event_content_type new_event_content;
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.object_key_id = event_content->applicative_content.object_key_id;
		new_event_content.destination_object_id = event_content->applicative_content.client_id;
		new_event_content.applicative_content.op_type = TX_GET_RETURN;
		server_send_message(state, &new_event_content, now);
		break;

	case CPU_TX_LOCAL_PUT: //return from cpu in case of local put
		if (pointer->configuration.server_verbose)
			printf("- function Server_ProcessEvent: received WRITE_VALUE_RET_CPU  at time %f\n", now);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction == NULL) {
			printf("ERROR: no transaction found with id %d (from client id %d)\n", event_content->applicative_content.tx_id, event_content->applicative_content.client_id);
			exit(-1);
		}
		transaction->executed_operations++;
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.server_id = pointer->server_id;
		new_event_content.applicative_content.op_type = TX_PUT_RETURN;
		new_event_content.destination_object_id = event_content->applicative_content.client_id;
		server_send_message(state, &new_event_content, now);
		if (pointer->configuration.server_verbose)
			printf("- function Server_ProcessEvent: sent DELIVER_PUT_RETURN to NET at time %f\n", now);
		break;

	case CPU_TX_REMOTE_GET_RETURN: //return from cpu in case of remote get

		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: CPU_REMOTE_TX_GET_RETURN received for tx %i run number %i at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		// check if a remote get response has already arrived
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction == NULL) {
			break;
		}
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.object_key_id = event_content->applicative_content.object_key_id;
		new_event_content.destination_object_id = new_event_content.applicative_content.client_id;
		new_event_content.applicative_content.op_type = TX_GET_RETURN;
		server_send_message(state, &new_event_content, now);

		break;

	case CPU_PREPARE: //return from cpu in case of prepare
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		transaction->expected_prepare_response_counter = 0;
		//stats for cubist

		//printf("\n%f",pointer->average_net_interarrival_tx_time);
		pointer->average_net_interarrival_tx_time=0.95*pointer->average_net_interarrival_tx_time+0.05*(now-pointer->last_net_iterarrival_tx_time);
		pointer->last_net_iterarrival_tx_time=now;
                
                if(transaction->write_set != NULL){
			//send prepare messages to other servers
			send_prepare_messages(state, transaction, event_content, now);
		}
		// check whether not to wait for other server response
		if (transaction->expected_prepare_response_counter == 0) {
			// can immediately commit
			if (pointer->configuration.server_verbose)
				printf("S%d - la tx %i ha solo write locali\n", me, event_content->applicative_content.tx_id);
			event_content->applicative_content.op_type = CPU_TX_DISTRIBUTED_FINAL_COMMIT;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_DISTRIBUTED_FINAL_TX_COMMIT to cpu for transaction %i at time %f\n", me, event_content->applicative_content.tx_id, now);
		}
		break;

	case CPU_TX_LOCAL_PREPARE_SUCCESSED: //return from cpu in case of local prepare
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.op_type = TX_PREPARE_SUCCEEDED;
		new_event_content.destination_object_id = new_event_content.applicative_content.server_id;
		server_send_prepare_response_message(state, &new_event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: sent TX_PREPARE_SUCCEEDED for transaction %i run number %i at time %f to server %i\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now,
					new_event_content.applicative_content.server_id);
		break;

	case CPU_TX_LOCAL_PREPARE_FAILED:
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.op_type = TX_PREPARE_FAILED;
		new_event_content.destination_object_id = new_event_content.applicative_content.server_id;
		server_send_prepare_response_message(state, &new_event_content, now);
		if (pointer->configuration.server_verbose)
			printf(" S%d - function Server_ProcessEvent: sent TX_PREPARE_FAILED for transaction %i at time %f to server %i\n", me, event_content->applicative_content.tx_id, now,
					new_event_content.applicative_content.server_id);
		break;

	case CPU_TX_PREPARE_FAILED:
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received CPU_PREPARE_FAILED  for transaction %i at time %f \n", me, event_content->applicative_content.tx_id, now);
		// transaction must abort
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction != NULL && transaction->current_tx_run_number == event_content->applicative_content.tx_run_number) {

			abort_distributed_transaction(state, transaction, event_content, now);
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			if (pointer->configuration.server_verbose)
				printf("S%d - function Server_ProcessEvent: transazione abortita at time %f\n", me, now);
			//reply to client
			event_content->destination_object_id = event_content->applicative_content.client_id;
			event_content->applicative_content.op_type = TX_EXECUTION_EXCEPTION;
			server_send_message(state, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - sent TX_EXECUTION_EXCEPTION for transaction %i to client %i at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.client_id,
						now);
		}
		break;

	case CPU_TX_DISTRIBUTED_FINAL_COMMIT:
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received CPU_DISTRIBUTED_FINAL_TX_COMMIT  at time %f for transaction %i\n", me, now, event_content->applicative_content.tx_id);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if(transaction->write_set != NULL){
			commit_distributed_transaction(state, transaction, event_content, now);
		}
		remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		//reply to client
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.applicative_content.op_type = TX_COMMIT_RESPONSE;
		new_event_content.destination_object_id = new_event_content.applicative_content.client_id;
		server_send_message(state, &new_event_content, now);
		break;

	case CPU_TX_LOCAL_FINAL_COMMIT:
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received CPU_LOCAL_TX_FINAL_COMMIT  at time %f for transaction %i\n", me, now, event_content->applicative_content.tx_id);
		commit_remote_transaction(state, event_content, now);
		break;

	case TX_LOCAL_TIMEOUT: // timeout for a local transaction

		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_LOCAL_TIMEOUT at time %f for transaction %d\n", me, now, event_content->applicative_content.tx_id);
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction != NULL && transaction->current_tx_run_number == event_content->applicative_content.tx_run_number && transaction->is_blocked) {
			event_content->applicative_content.op_type = TX_LOCAL_ABORT;
			CC_control(event_content, state, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - function Server_ProcessEvent: transaction aborted at time %f\n", me, now);
			event_content->applicative_content.op_type = CPU_TX_LOCAL_ABORT;
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_LOCAL_ABORT added to CPU at time %f\n", me, now);
		}
		break;

	case TX_PREPARE_TIMEOUT: // timeout for a prepare of a remote transaction
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_PREPARE_TIMEOUT at time %f for transaction %d run number %i \n", me, now, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number);
		// remove waiting event of transaction
		transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (transaction != NULL && transaction->current_tx_run_number == event_content->applicative_content.tx_run_number && transaction->is_blocked) {
			event_content->applicative_content.op_type = TX_PREPARE_ABORT;
			CC_control(event_content, state, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - function Server_ProcessEvent: prepare of %i transaction aborted at time %f\n", me, event_content->applicative_content.tx_id, now);
			remove_transaction_metadata(event_content->applicative_content.tx_id, pointer);
			//reply to server
			memcpy(&new_event_content, event_content, sizeof(event_content_type));
			new_event_content.destination_object_id = event_content->applicative_content.server_id;
			new_event_content.applicative_content.op_type = TX_PREPARE_FAILED;
			server_send_message(state, &new_event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - Server_ProcessEvent: transaction %i aborted at time %f per LOCK_TIMEOUT\n", me, event_content->applicative_content.tx_id, now);
			event_content->applicative_content.op_type = CPU_TX_REMOTE_ABORT;
			add_processing_request(pointer, event_content, now);
			if (pointer->configuration.server_verbose)
				printf("S%d - CPU_TX_REMOTE_ABORT added to CPU at time %f\n", me, now);
		}
		break;

	case CPU_TX_BEGIN: //return from cpu in case of begin
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received TX_BEGIN_RET_CPU  at time %f\n", me, now);
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.destination_object_id = event_content->applicative_content.client_id;
		new_event_content.applicative_content.op_type = TX_BEGIN_RETURN;
		server_send_message(state, &new_event_content, now);
	 	transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer);
		if (pointer->configuration.server_verbose)
			printf("S%d - Server_ProcessEvent: TX_BEGIN_RETURN sent at time %f\n", me, now);
		break;

	case CPU_TX_LOCAL_ABORT: //return from cpu in case of local transaction abort
		if (pointer->configuration.server_verbose)
			printf("S%d - tx %i run number %i aborted at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number, now);
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		//reply to client
		new_event_content.destination_object_id = event_content->applicative_content.client_id;
		new_event_content.applicative_content.op_type = TX_EXECUTION_EXCEPTION;
		server_send_message(state, &new_event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - TX_EXECUTION_EXCEPTION of transaction %i sent to client %i at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.client_id, now);
		break;

	case CPU_TX_REMOTE_ABORT: //return from cpu in case of remote transaction abort
		abort_remote_transaction(state, event_content, now);
		break;

	case CPU_TX_LOCAL_GET_FROM_REMOTE: //return from cpu in case of local get from a remote transaction
		//send get response to the server
		if (pointer->configuration.server_verbose)
			printf("S%d - function Server_ProcessEvent: received CPU_LOCAL_TX_GET_FROM_REMOTE  at time %f\n", me, now);
		memcpy(&new_event_content, event_content, sizeof(event_content_type));
		new_event_content.destination_object_id = event_content->applicative_content.server_id;
		new_event_content.applicative_content.op_type = TX_REMOTE_GET_RETURN;
		server_send_message(state, &new_event_content, now);
		if (pointer->configuration.server_verbose)
			printf("S%d - TX_REMOTE_GET_RETURN sent to NET for tx %i run number %i at time %f\n", me, event_content->applicative_content.tx_id, event_content->applicative_content.tx_run_number,
					now);
		break;

	case CPU_TX_SEND_REMOTE_GET: //return from cpu in case of response to a remote get request
		if (pointer->configuration.server_verbose)
			printf("S%d - CPU_SEND_REMOTE_TX_GET received at time %f\n", me, now);
		send_remote_tx_get(state, event_content, now);
		break;

	case CPU_PROCESSING_REQUEST_EXECUTED: //notify a request completion to cpu
		cpu_request_executed(pointer, now);
		break;

	case DELIVER_MESSAGE: // a message from network has been received...
		process_message(me, now, event_content, state);
		break;

	default:
		printf("ERROR: event type %i not managed\n", event_type);
		exit(-1);
		break;
	}
}