This repository contains a multithreaded in-memory key/value store built with a client-server architecture in C++. The project features a concurrent server capable of managing multiple simultaneous client connections over a network, performing atomic data operations on named, thread-safe tables.
This project was originally developed as a successful partner project for a Computer Systems Fundamentals class. It demonstrates an understanding of concurrent programming, POSIX threading (pthreads), network socket programming, and managing transactions to ensure data consistency.
The system is separated into modular components to handle network execution and data concurrency:
- Multithreaded Server (
Server&ClientConnection): A main thread listens for incoming TCP connections and spawns dedicated, detached POSIX worker pthreads for each client. This isolates client logic, allowing the server to handle simultaneous concurrent connections without blocking. - Core Data Structures (
Table&ValueStack): Key/value data is managed in memory. Operations support multi-step transactions (BEGIN, COMMIT, ROLLBACK). - Transactional Logic: The
Tableclass maintains two states (committed data and pending changes). Reads within an active transaction check pending data first to keep the data consistent. Changes only become permanent upon an explicitCOMMIT. - Protocol & Serialization (
Message): A custom, line-oriented text protocol dictates client/server interaction. The server handles the encoding and decoding of network strings into commands.
In a multi-threaded server architecture, preventing data corruption and deadlocks was critical:
- Global Table Map Locking: A global mutex protects the server's map of active tables so creating a new table doesn't cause a race condition. A helper
Guardclass makes sure this lock is always released, even if an error occurs. - Individual Table Locks: Each
Tablehas its own mutex lock. This means a client modifying one table won't block other clients from using different tables at the same time. - Deadlock Avoidance:
- Autocommit Mode: Single operations only hold one lock at a time, which naturally prevents circular deadlocks.
- Transaction Mode: Multi-step transactions use non-blocking "trylocks" (
table->trylock()). If a lock is already taken by another client, the transaction fails safely, undoes its changes, and drops all its locks so it doesn't hold up the rest of the system.
server.cpp/client_connection.cpp- Multithreaded server and client connection handling. The original assignment code files are preserved.table.cpp/value_stack.cpp- Data structures for the tables and managing client operations.message.cpp/message_serialization.cpp- Communication protocol for sending and receiving commands.Makefile- Command-line build configuration.csapp.h/csapp.c- I/O and networking wrappers.
- Programming Language: C++
- Concepts: Multithreading (pthreads), Network Sockets, Mutexes, Client-Server Architecture.
- Environment: Tested and compiled on x86 Linux servers (University environments).