#include <iostream>
#include <cstring>
#include <>
#include <sys/>
#include <netinet/in.h>
#include <arpa/>
#include <>
#include <>
#include <cstdlib>
#include <>
#include <>
// Define two mutually exclusive locks
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
// Functions that handle client requests
void* handle_client(void* arg) {
int client_socket = *(static_cast<int*>(arg));
free(arg); // Release dynamically allocated memory passed to a thread
std::cout << "Client connected with socket: " << client_socket << std::endl;
// Simulate client request processing
if (client_socket == 4) {
// Client 1: get mutex1, then get mutex2
std::cout << "Client " << client_socket << ": Trying to lock mutex1..." << std::endl;
pthread_mutex_lock(&mutex1);
usleep(5000000); // Hibernate 5 seconds
std::cout << "Client " << client_socket << ": Locked mutex1, now trying to lock mutex2..." << std::endl;
// Trying to get mutex2
pthread_mutex_lock(&mutex2); // deadlock point
usleep(5000000); // Hibernate 5 seconds
std::cout << "Client " << client_socket << ": Locked both mutex1 and mutex2" << std::endl;
// Releasing a Mutual Exclusion Lock
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
} else if (client_socket == 5) {
// Client 2: get mutex2 first, then mutex1
std::cout << "Client " << client_socket << ": Trying to lock mutex2..." << std::endl;
pthread_mutex_lock(&mutex2);
usleep(5000000); // Hibernate 5 seconds
std::cout << "Client " << client_socket << ": Locked mutex2, now trying to lock mutex1..." << std::endl;
// Trying to get mutex1
pthread_mutex_lock(&mutex1); // deadlock point
usleep(5000000); // Hibernate 5 seconds
std::cout << "Client " << client_socket << ": Locked both mutex1 and mutex2" << std::endl;
// Releasing a Mutual Exclusion Lock
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
}
// Closing a client connection
close(client_socket);
std::cout << "Client disconnected with socket: " << client_socket << std::endl;
pthread_exit(NULL);
}
// TCP Server Master Functions
void start_server(int port) {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// Creating a socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Setting the SO_REUSEADDR option
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// Bind the socket to the specified port
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// monitor a connection
if (listen(server_fd, 3) < 0) {
perror("listen failed");
exit(EXIT_FAILURE);
}
std::cout << "Server started on port " << port << ". Waiting for connections..." << std::endl;
int client_id = 1; // Used to distinguish between different clients
while (true) {
// Accepting new client connections
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
continue;
}
// Create a new thread for each client
pthread_t thread;
int* client_socket_ptr = new int(new_socket); // Dynamically allocate memory for storing socket descriptors
if (pthread_create(&thread, NULL, handle_client, static_cast<void*>(client_socket_ptr)) != 0) {
perror("pthread_create failed");
delete client_socket_ptr; // If thread creation fails, free memory
close(new_socket);
continue;
}
// Separate threads and allow them to run independently
pthread_detach(thread);
// To test for deadlocks, only the first two client connections are accepted
if (client_id >= 3) {
close(new_socket); // Close redundant connections
continue;
}
client_id++;
}
}
int main() {
int port = 8080;
start_server(port);
return 0;
}