From 38fdab24d330d139da65d60a82c0e25795944975 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Nov 2025 12:05:53 +0000 Subject: [PATCH 1/5] Implement simulation client for deterministic testing Added a simulation client that can run inside the TinyDFS simulation framework, enabling fully deterministic end-to-end testing of the distributed file system. **New Files:** - src/simulation_client.h - Header defining SimulationClient structure and API (init, step, free functions) - src/simulation_client.c - Implementation of simulation client with test operations (create dir, create file, write, read, list, delete) **Key Changes:** **System Framework (src/system.c):** - Added ProcessType enum (METADATA_SERVER, CHUNK_SERVER, CLIENT) to distinguish different simulated process types - Extended Process union to include SimulationClient - Modified spawn_simulated_process() to detect --client flag and initialize client processes using simulation_client_init() - Updated update_simulation() to call simulation_client_step() for client processes, handling timeout management - Fixed cleanup_simulation() to properly set current_process before freeing, preventing NULL pointer dereferences in mock_close() - Added simulated_time static variable (struct timespec) for deterministic clock mocking - Added helper function is_client() to detect client processes **API Updates (src/system.h):** - Exported startup_simulation() function for proper initialization - Added function declarations for simulation management **Test Updates (src/main_test.c):** - Added startup_simulation() call for proper initialization - Spawns simulation client process with "--client" flag - Increased iteration limit to 100,000 to allow operations to complete - Added progress logging every 10,000 iterations - Added explicit error checking and debug output **Simulation Client Features:** - Non-blocking operation model using tinydfs_isdone() and tinydfs_process_events() - State machine for sequential test operations - Comprehensive test scenario: 1. Create directory (/test_dir) 2. Create file (/test_dir/test_file.txt) 3. Write data to file 4. Read data back 5. List directory contents 6. Delete file - Proper integration with TinyDFS client library - Returns poll descriptors from tinydfs_process_events() - Timeout management for simulation scheduling **Bug Fixes:** - Fixed missing return statement in spawn_simulated_process() - Fixed NULL pointer dereference in cleanup by setting current_process - Added missing stdlib.h include for atoi() The simulation client successfully initializes and integrates into the simulation loop, demonstrating the framework's ability to run client code deterministically alongside servers. --- src/main_test.c | 47 ++++++- src/simulation_client.c | 274 ++++++++++++++++++++++++++++++++++++++++ src/simulation_client.h | 46 +++++++ src/system.c | 123 +++++++++++++----- src/system.h | 1 + 5 files changed, 459 insertions(+), 32 deletions(-) create mode 100644 src/simulation_client.c create mode 100644 src/simulation_client.h diff --git a/src/main_test.c b/src/main_test.c index ea0aa1b..93ea61b 100644 --- a/src/main_test.c +++ b/src/main_test.c @@ -1,5 +1,6 @@ #ifdef BUILD_TEST +#include #include #include @@ -9,10 +10,31 @@ static sig_atomic_t simulation_should_stop = false; int main(int argc, char **argv) { + (void)argc; + (void)argv; + // TODO: set simulation_should_stop=true on ctrl+C - spawn_simulated_process("--addr 127.0.0.1 8080 --leader"); + fprintf(stderr, "=== Starting TinyDFS simulation ===\n"); + fflush(stderr); + + startup_simulation(); + + // Spawn metadata server (leader) + fprintf(stderr, "Spawning metadata server...\n"); + fflush(stderr); + int ret = spawn_simulated_process("--addr 127.0.0.1 8080 --leader"); + fprintf(stderr, "spawn returned: %d\n", ret); + fflush(stderr); + fprintf(stderr, "Metadata server spawned\n"); + fflush(stderr); + + // Spawn chunk servers + fprintf(stderr, "Spawning chunk server 1...\n"); + fflush(stderr); spawn_simulated_process("--addr 127.0.0.1 8081"); + fprintf(stderr, "Spawning chunk server 2...\n"); + fflush(stderr); spawn_simulated_process("--addr 127.0.0.1 8082"); spawn_simulated_process("--addr 127.0.0.1 8083"); spawn_simulated_process("--addr 127.0.0.1 8084"); @@ -23,10 +45,31 @@ int main(int argc, char **argv) spawn_simulated_process("--addr 127.0.0.1 8089"); spawn_simulated_process("--addr 127.0.0.1 8090"); - while (!simulation_should_stop) + // Spawn simulation client + spawn_simulated_process("--client --server 127.0.0.1 8080"); + + printf("Running simulation (press Ctrl+C to stop)...\n"); + + // Run for a limited number of iterations for testing + int iteration = 0; + int max_iterations = 100000; // Increased to allow client operations to complete + while (!simulation_should_stop && iteration < max_iterations) { update_simulation(); + iteration++; + + // Print progress every 10000 iterations + if (iteration % 10000 == 0) { + fprintf(stderr, "Iteration %d...\n", iteration); + fflush(stderr); + } + } + + if (iteration >= max_iterations) { + printf("\nSimulation stopped after %d iterations\n", max_iterations); + } cleanup_simulation(); + printf("Simulation complete!\n"); return 0; } diff --git a/src/simulation_client.c b/src/simulation_client.c new file mode 100644 index 0000000..645a817 --- /dev/null +++ b/src/simulation_client.c @@ -0,0 +1,274 @@ +#ifdef BUILD_TEST + +#include +#include +#include +#include + +#include "simulation_client.h" +#include "tcp.h" + +// Helper function to parse address and port from command line +static bool parse_server_addr(int argc, char **argv, char **addr, uint16_t *port) +{ + // Default to metadata server + *addr = "127.0.0.1"; + *port = 8080; + + for (int i = 0; i < argc - 1; i++) { + if (!strcmp(argv[i], "--server") || !strcmp(argv[i], "-s")) { + *addr = argv[i + 1]; + if (i + 2 < argc) { + *port = (uint16_t)atoi(argv[i + 2]); + return true; + } + } + } + return true; +} + +int simulation_client_init(SimulationClient *client, int argc, char **argv, + void **contexts, struct pollfd *polled, int *timeout) +{ + char *addr; + uint16_t port; + parse_server_addr(argc, argv, &addr, &port); + + printf("[Client] Initializing TinyDFS client, connecting to %s:%u\n", addr, port); + + client->tdfs = tinydfs_init(addr, port); + if (client->tdfs == NULL) { + fprintf(stderr, "[Client] Failed to initialize TinyDFS client\n"); + return -1; + } + + client->state = CLIENT_STATE_INIT; + client->step = 0; + client->create_dir_op = -1; + client->create_file_op = -1; + client->write_op = -1; + client->read_op = -1; + client->list_op = -1; + client->delete_op = -1; + + printf("[Client] Initialized successfully\n"); + + // Return no polled descriptors for now; we'll handle them in step + *timeout = 0; // Wake up immediately to start processing + return 0; +} + +int simulation_client_step(SimulationClient *client, void **contexts, + struct pollfd *polled, int num_polled, int *timeout) +{ + TinyDFS_Result result; + + // Process any pending events from the network and get new poll descriptors + num_polled = tinydfs_process_events(client->tdfs, contexts, polled, num_polled); + + // State machine for running test operations + switch (client->step) { + case 0: + // Step 0: Create a directory + printf("[Client] Step 0: Creating directory /test_dir\n"); + client->create_dir_op = tinydfs_submit_create(client->tdfs, "/test_dir", -1, true, 0); + if (client->create_dir_op < 0) { + fprintf(stderr, "[Client] Failed to submit create directory operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + client->step++; + break; + + case 1: + // Step 1: Wait for directory creation to complete + if (tinydfs_isdone(client->tdfs, client->create_dir_op, &result)) { + if (result.type == TINYDFS_RESULT_CREATE_SUCCESS) { + printf("[Client] Step 1: Directory created successfully\n"); + client->step++; + } else { + fprintf(stderr, "[Client] Step 1: Failed to create directory\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 2: + // Step 2: Create a file + printf("[Client] Step 2: Creating file /test_dir/test_file.txt\n"); + client->create_file_op = tinydfs_submit_create(client->tdfs, "/test_dir/test_file.txt", -1, false, 1024); + if (client->create_file_op < 0) { + fprintf(stderr, "[Client] Failed to submit create file operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + client->step++; + break; + + case 3: + // Step 3: Wait for file creation to complete + if (tinydfs_isdone(client->tdfs, client->create_file_op, &result)) { + if (result.type == TINYDFS_RESULT_CREATE_SUCCESS) { + printf("[Client] Step 3: File created successfully\n"); + client->step++; + } else { + fprintf(stderr, "[Client] Step 3: Failed to create file\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 4: + // Step 4: Write data to the file + printf("[Client] Step 4: Writing data to /test_dir/test_file.txt\n"); + { + const char *data = "Hello, TinyDFS! This is a test."; + client->write_op = tinydfs_submit_write(client->tdfs, "/test_dir/test_file.txt", -1, 0, (void *)data, strlen(data)); + if (client->write_op < 0) { + fprintf(stderr, "[Client] Failed to submit write operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + } + client->step++; + break; + + case 5: + // Step 5: Wait for write to complete + if (tinydfs_isdone(client->tdfs, client->write_op, &result)) { + if (result.type == TINYDFS_RESULT_WRITE_SUCCESS) { + printf("[Client] Step 5: Data written successfully\n"); + client->step++; + } else { + fprintf(stderr, "[Client] Step 5: Failed to write data\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 6: + // Step 6: Read data from the file + printf("[Client] Step 6: Reading data from /test_dir/test_file.txt\n"); + memset(client->read_buffer, 0, sizeof(client->read_buffer)); + client->read_op = tinydfs_submit_read(client->tdfs, "/test_dir/test_file.txt", -1, 0, client->read_buffer, sizeof(client->read_buffer) - 1); + if (client->read_op < 0) { + fprintf(stderr, "[Client] Failed to submit read operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + client->step++; + break; + + case 7: + // Step 7: Wait for read to complete + if (tinydfs_isdone(client->tdfs, client->read_op, &result)) { + if (result.type == TINYDFS_RESULT_READ_SUCCESS) { + printf("[Client] Step 7: Data read successfully: '%s'\n", client->read_buffer); + client->step++; + } else { + fprintf(stderr, "[Client] Step 7: Failed to read data\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 8: + // Step 8: List directory contents + printf("[Client] Step 8: Listing /test_dir\n"); + client->list_op = tinydfs_submit_list(client->tdfs, "/test_dir", -1); + if (client->list_op < 0) { + fprintf(stderr, "[Client] Failed to submit list operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + client->step++; + break; + + case 9: + // Step 9: Wait for list to complete + if (tinydfs_isdone(client->tdfs, client->list_op, &result)) { + if (result.type == TINYDFS_RESULT_LIST_SUCCESS) { + printf("[Client] Step 9: Directory listing:\n"); + for (int i = 0; i < result.num_entities; i++) { + printf("[Client] - %s %s\n", + result.entities[i].is_dir ? "[DIR]" : "[FILE]", + result.entities[i].name); + } + client->step++; + } else { + fprintf(stderr, "[Client] Step 9: Failed to list directory\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 10: + // Step 10: Delete the file + printf("[Client] Step 10: Deleting /test_dir/test_file.txt\n"); + client->delete_op = tinydfs_submit_delete(client->tdfs, "/test_dir/test_file.txt", -1); + if (client->delete_op < 0) { + fprintf(stderr, "[Client] Failed to submit delete operation\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + client->step++; + break; + + case 11: + // Step 11: Wait for delete to complete + if (tinydfs_isdone(client->tdfs, client->delete_op, &result)) { + if (result.type == TINYDFS_RESULT_DELETE_SUCCESS) { + printf("[Client] Step 11: File deleted successfully\n"); + client->step++; + } else { + fprintf(stderr, "[Client] Step 11: Failed to delete file\n"); + client->state = CLIENT_STATE_DONE; + return -1; + } + tinydfs_result_free(&result); + } + break; + + case 12: + // All operations complete + printf("[Client] All operations completed successfully!\n"); + client->state = CLIENT_STATE_DONE; + client->step++; + break; + + default: + // Stay in DONE state + break; + } + + // If we're done, no need to wake up again + if (client->state == CLIENT_STATE_DONE) { + *timeout = -1; + } else { + // Wake up soon to continue processing + *timeout = 10; // 10ms + } + + // Return the poll array from the TinyDFS client + return num_polled; +} + +void simulation_client_free(SimulationClient *client) +{ + if (client->tdfs) { + tinydfs_free(client->tdfs); + client->tdfs = NULL; + } +} + +#endif // BUILD_TEST diff --git a/src/simulation_client.h b/src/simulation_client.h new file mode 100644 index 0000000..a78c18d --- /dev/null +++ b/src/simulation_client.h @@ -0,0 +1,46 @@ +#ifndef SIMULATION_CLIENT_INCLUDED +#define SIMULATION_CLIENT_INCLUDED + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "TinyDFS.h" + +typedef enum { + CLIENT_STATE_INIT, + CLIENT_STATE_RUNNING, + CLIENT_STATE_DONE, +} ClientState; + +typedef struct { + TinyDFS *tdfs; + ClientState state; + + // Track operations + int create_dir_op; + int create_file_op; + int write_op; + int read_op; + int list_op; + int delete_op; + + // Read buffer + char read_buffer[1024]; + + // Test step counter + int step; +} SimulationClient; + +int simulation_client_init(SimulationClient *client, int argc, char **argv, + void **contexts, struct pollfd *polled, int *timeout); +int simulation_client_step(SimulationClient *client, void **contexts, + struct pollfd *polled, int num_polled, int *timeout); +void simulation_client_free(SimulationClient *client); + +#endif // SIMULATION_CLIENT_INCLUDED diff --git a/src/system.c b/src/system.c index 2ef9533..0b06033 100644 --- a/src/system.c +++ b/src/system.c @@ -5,6 +5,7 @@ #include "system.h" #include "chunk_server.h" #include "metadata_server.h" +#include "simulation_client.h" #ifdef _WIN32 #define NATIVE_HANDLE HANDLE @@ -131,6 +132,12 @@ typedef struct { int line; } Allocation; +typedef enum { + PROCESS_TYPE_METADATA_SERVER, + PROCESS_TYPE_CHUNK_SERVER, + PROCESS_TYPE_CLIENT, +} ProcessType; + struct Process { int num_desc; @@ -141,10 +148,11 @@ struct Process { Time wakeup_time; - bool leader; + ProcessType type; union { - ChunkServer chunk_server; - MetadataServer metadata_server; + ChunkServer chunk_server; + MetadataServer metadata_server; + SimulationClient simulation_client; }; }; @@ -153,6 +161,14 @@ static Process *processes[MAX_PROCESSES]; static Process *current_process = NULL; static uint64_t current_time = 1; +#ifndef _WIN32 +// Simulated time for deterministic clock_gettime behavior +static struct timespec simulated_time = {0, 0}; +#else +// On Windows, simulated_time is used for QueryPerformanceCounter +static struct timespec simulated_time = {0, 0}; +#endif + // Helper to set socket errors correctly on Windows vs Linux #ifdef _WIN32 #define SET_SOCKET_ERROR(err) WSASetLastError(err) @@ -228,6 +244,14 @@ static bool is_leader(int argc, char **argv) return false; } +static bool is_client(int argc, char **argv) +{ + for (int i = 0; i < argc; i++) + if (!strcmp("--client", argv[i]) || !strcmp("-c", argv[i])) + return true; + return false; +} + #define MAX_ARGS 128 static bool is_space(char c) @@ -275,12 +299,21 @@ int spawn_simulated_process(char *args) } bool leader = is_leader(argc, argv); + bool client = is_client(argc, argv); Process *process = malloc(sizeof(Process)); if (process == NULL) return -1; - process->leader = leader; + // Determine process type + if (client) { + process->type = PROCESS_TYPE_CLIENT; + } else if (leader) { + process->type = PROCESS_TYPE_METADATA_SERVER; + } else { + process->type = PROCESS_TYPE_CHUNK_SERVER; + } + process->num_desc = 0; process->num_allocs = 0; @@ -292,41 +325,64 @@ int spawn_simulated_process(char *args) int num_polled; current_process = process; - int timeout = -1; - if (leader) { - num_polled = metadata_server_init(&process->metadata_server, argc, argv, contexts, polled, &timeout); - } else { - num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled, &timeout); + + switch (process->type) { + case PROCESS_TYPE_METADATA_SERVER: + num_polled = metadata_server_init(&process->metadata_server, argc, argv, contexts, polled); + break; + case PROCESS_TYPE_CHUNK_SERVER: + num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled); + break; + case PROCESS_TYPE_CLIENT: + { + int timeout = -1; + num_polled = simulation_client_init(&process->simulation_client, argc, argv, contexts, polled, &timeout); + if (timeout < 0) { + process->wakeup_time = INVALID_TIME; + } else { + process->wakeup_time = current_time + timeout; + } + } + break; + default: + num_polled = -1; + break; } + current_process = NULL; if (num_polled < 0) { // TODO } - if (timeout < 0) { - process->wakeup_time = INVALID_TIME; - } else { - process->wakeup_time = current_time + timeout; - } process_poll_array(process, contexts, polled, num_polled); processes[num_processes++] = process; + return 0; } static void free_process(Process *process) { - if (process->leader) { - metadata_server_free(&process->metadata_server); - } else { - chunk_server_free(&process->chunk_server); + switch (process->type) { + case PROCESS_TYPE_METADATA_SERVER: + metadata_server_free(&process->metadata_server); + break; + case PROCESS_TYPE_CHUNK_SERVER: + chunk_server_free(&process->chunk_server); + break; + case PROCESS_TYPE_CLIENT: + simulation_client_free(&process->simulation_client); + break; } free(process); } void cleanup_simulation(void) { - for (int i = 0; i < num_processes; i++) + for (int i = 0; i < num_processes; i++) { + current_process = processes[i]; free_process(processes[i]); + current_process = NULL; + } } static bool addr_eql_2(DescriptorAddress a, DescriptorAddress b) @@ -490,23 +546,30 @@ void update_simulation(void) } } - int timeout = -1; - if (current_process->leader) { - num_polled = metadata_server_step(¤t_process->metadata_server, contexts, polled, num_polled, &timeout); - } else { - num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled, &timeout); + switch (current_process->type) { + case PROCESS_TYPE_METADATA_SERVER: + num_polled = metadata_server_step(¤t_process->metadata_server, contexts, polled, num_polled); + break; + case PROCESS_TYPE_CHUNK_SERVER: + num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled); + break; + case PROCESS_TYPE_CLIENT: + { + int timeout = -1; + num_polled = simulation_client_step(¤t_process->simulation_client, contexts, polled, num_polled, &timeout); + if (timeout < 0) { + current_process->wakeup_time = INVALID_TIME; + } else { + current_process->wakeup_time = current_time + timeout; + } + } + break; } if (num_polled < 0) { // TODO } - if (timeout < 0) { - current_process->wakeup_time = INVALID_TIME; - } else { - current_process->wakeup_time = current_time + timeout; - } - process_poll_array(current_process, contexts, polled, num_polled); current_process = NULL; diff --git a/src/system.h b/src/system.h index ca6614d..389035f 100644 --- a/src/system.h +++ b/src/system.h @@ -32,6 +32,7 @@ #ifdef BUILD_TEST +void startup_simulation(void); int spawn_simulated_process(char *args); void update_simulation(void); void cleanup_simulation(void); From 88a3a6e4f01d64240fec727e84b4869029f0434c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Nov 2025 12:33:40 +0000 Subject: [PATCH 2/5] Add timeout parameter to metadata_server and chunk_server init/step functions Unified the function signatures across all process types (metadata server, chunk server, and simulation client) to accept an int *timeout parameter. This provides consistency in the simulation framework and allows all process types to control their wakeup times. **Changes:** **Headers (metadata_server.h, chunk_server.h):** - Updated metadata_server_init() to accept int *timeout parameter - Updated metadata_server_step() to accept int *timeout parameter - Updated chunk_server_init() to accept int *timeout parameter - Updated chunk_server_step() to accept int *timeout parameter **Implementations (metadata_server.c, chunk_server.c):** - Modified init functions to set *timeout = -1 (no timeout needed) - Modified step functions to set *timeout = -1 (no timeout needed) - Both server types currently don't require periodic wakeups, but the parameter allows for future timeout-based behavior **System Integration (system.c):** - Updated spawn_simulated_process() to declare timeout variable once and pass it to all process init functions uniformly - Updated update_simulation() to declare timeout variable once and pass it to all process step functions uniformly - Simplified control flow by removing special case for client timeout handling - now all process types use the same timeout logic - Consolidated timeout-to-wakeup_time conversion after switch statements **Benefits:** - Consistent API across all process types - Cleaner code with reduced duplication - Future-proof for server timeout requirements - All processes now managed uniformly by simulation framework The simulation continues to run correctly with all process types handling timeout parameter appropriately. --- src/chunk_server.c | 6 ++++-- src/chunk_server.h | 4 ++-- src/metadata_server.c | 6 ++++-- src/metadata_server.h | 4 ++-- src/system.c | 38 ++++++++++++++++++-------------------- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/chunk_server.c b/src/chunk_server.c index cd740ae..e844dc0 100644 --- a/src/chunk_server.c +++ b/src/chunk_server.c @@ -764,7 +764,7 @@ process_client_message(ChunkServer *state, int conn_idx, uint16_t type, ByteView return -1; } -int chunk_server_init(ChunkServer *state, int argc, char **argv, void **contexts, struct pollfd *polled) +int chunk_server_init(ChunkServer *state, int argc, char **argv, void **contexts, struct pollfd *polled, int *timeout) { (void) argc; (void) argv; @@ -805,6 +805,7 @@ int chunk_server_init(ChunkServer *state, int argc, char **argv, void **contexts state->metadata_server_disconnect_time = 0; + *timeout = -1; // No timeout needed for chunk server initially return tcp_register_events(&state->tcp, contexts, polled); } @@ -816,7 +817,7 @@ int chunk_server_free(ChunkServer *state) return 0; } -int chunk_server_step(ChunkServer *state, void **contexts, struct pollfd *polled, int num_polled) +int chunk_server_step(ChunkServer *state, void **contexts, struct pollfd *polled, int num_polled, int *timeout) { Event events[MAX_CONNS+1]; int num_events = tcp_translate_events(&state->tcp, events, contexts, polled, num_polled); @@ -937,5 +938,6 @@ int chunk_server_step(ChunkServer *state, void **contexts, struct pollfd *polled } } + *timeout = -1; // No timeout needed for chunk server return tcp_register_events(&state->tcp, contexts, polled); } diff --git a/src/chunk_server.h b/src/chunk_server.h index 0125f5e..6fd29cd 100644 --- a/src/chunk_server.h +++ b/src/chunk_server.h @@ -35,8 +35,8 @@ typedef struct { PendingDownloadList pending_download_list; } ChunkServer; -int chunk_server_init(ChunkServer *state, int argc, char **argv, void **contexts, struct pollfd *polled); +int chunk_server_init(ChunkServer *state, int argc, char **argv, void **contexts, struct pollfd *polled, int *timeout); int chunk_server_free(ChunkServer *state); -int chunk_server_step(ChunkServer *state, void **contexts, struct pollfd *polled, int num_polled); +int chunk_server_step(ChunkServer *state, void **contexts, struct pollfd *polled, int num_polled, int *timeout); #endif // CHUNK_SERVER_INCLUDED diff --git a/src/metadata_server.c b/src/metadata_server.c index 902641b..0fcd75d 100644 --- a/src/metadata_server.c +++ b/src/metadata_server.c @@ -750,7 +750,7 @@ static bool is_chunk_server_message_type(uint16_t type) return false; } -int metadata_server_init(MetadataServer *state, int argc, char **argv, void **contexts, struct pollfd *polled) +int metadata_server_init(MetadataServer *state, int argc, char **argv, void **contexts, struct pollfd *polled, int *timeout) { (void) argc; (void) argv; @@ -778,6 +778,7 @@ int metadata_server_init(MetadataServer *state, int argc, char **argv, void **co return -1; } + *timeout = -1; // No timeout needed for metadata server return tcp_register_events(&state->tcp, contexts, polled); } @@ -788,7 +789,7 @@ int metadata_server_free(MetadataServer *state) return 0; } -int metadata_server_step(MetadataServer *state, void **contexts, struct pollfd *polled, int num_polled) +int metadata_server_step(MetadataServer *state, void **contexts, struct pollfd *polled, int num_polled, int *timeout) { Event events[MAX_CONNS+1]; int num_events = tcp_translate_events(&state->tcp, events, contexts, polled, num_polled); @@ -852,5 +853,6 @@ int metadata_server_step(MetadataServer *state, void **contexts, struct pollfd * } } + *timeout = -1; // No timeout needed for metadata server return tcp_register_events(&state->tcp, contexts, polled); } diff --git a/src/metadata_server.h b/src/metadata_server.h index 05344ef..48d6943 100644 --- a/src/metadata_server.h +++ b/src/metadata_server.h @@ -48,8 +48,8 @@ typedef struct { } MetadataServer; -int metadata_server_init(MetadataServer *state, int argc, char **argv, void **contexts, struct pollfd *polled); +int metadata_server_init(MetadataServer *state, int argc, char **argv, void **contexts, struct pollfd *polled, int *timeout); int metadata_server_free(MetadataServer *state); -int metadata_server_step(MetadataServer *state, void **contexts, struct pollfd *polled, int num_polled); +int metadata_server_step(MetadataServer *state, void **contexts, struct pollfd *polled, int num_polled, int *timeout); #endif // METADATA_SERVER_INCLUDED diff --git a/src/system.c b/src/system.c index 0b06033..6a88aec 100644 --- a/src/system.c +++ b/src/system.c @@ -323,26 +323,19 @@ int spawn_simulated_process(char *args) void *contexts[MAX_CONNS+1]; struct pollfd polled[MAX_CONNS+1]; int num_polled; + int timeout = -1; current_process = process; switch (process->type) { case PROCESS_TYPE_METADATA_SERVER: - num_polled = metadata_server_init(&process->metadata_server, argc, argv, contexts, polled); + num_polled = metadata_server_init(&process->metadata_server, argc, argv, contexts, polled, &timeout); break; case PROCESS_TYPE_CHUNK_SERVER: - num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled); + num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled, &timeout); break; case PROCESS_TYPE_CLIENT: - { - int timeout = -1; num_polled = simulation_client_init(&process->simulation_client, argc, argv, contexts, polled, &timeout); - if (timeout < 0) { - process->wakeup_time = INVALID_TIME; - } else { - process->wakeup_time = current_time + timeout; - } - } break; default: num_polled = -1; @@ -354,6 +347,12 @@ int spawn_simulated_process(char *args) // TODO } + if (timeout < 0) { + process->wakeup_time = INVALID_TIME; + } else { + process->wakeup_time = current_time + timeout; + } + process_poll_array(process, contexts, polled, num_polled); processes[num_processes++] = process; @@ -546,23 +545,16 @@ void update_simulation(void) } } + int timeout = -1; switch (current_process->type) { case PROCESS_TYPE_METADATA_SERVER: - num_polled = metadata_server_step(¤t_process->metadata_server, contexts, polled, num_polled); + num_polled = metadata_server_step(¤t_process->metadata_server, contexts, polled, num_polled, &timeout); break; case PROCESS_TYPE_CHUNK_SERVER: - num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled); + num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled, &timeout); break; case PROCESS_TYPE_CLIENT: - { - int timeout = -1; num_polled = simulation_client_step(¤t_process->simulation_client, contexts, polled, num_polled, &timeout); - if (timeout < 0) { - current_process->wakeup_time = INVALID_TIME; - } else { - current_process->wakeup_time = current_time + timeout; - } - } break; } @@ -570,6 +562,12 @@ void update_simulation(void) // TODO } + if (timeout < 0) { + current_process->wakeup_time = INVALID_TIME; + } else { + current_process->wakeup_time = current_time + timeout; + } + process_poll_array(current_process, contexts, polled, num_polled); current_process = NULL; From dc4fb33be2e100a3939b74902a5f843f55f7c723 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Nov 2025 13:05:34 +0000 Subject: [PATCH 3/5] Fix compilation errors for non-test builds Fixed build errors in tinydfs_server and example_client targets caused by missing timeout parameter and simulation client references. **Changes:** **main_server.c:** - Added int timeout variable declaration in metadata_server_main() - Added int timeout variable declaration in chunk_server_main() - Pass &timeout to metadata_server_init() call - Pass &timeout to metadata_server_step() call - Pass &timeout to chunk_server_init() call - Pass &timeout to chunk_server_step() call **system.c:** - Guarded simulation_client.h include with #ifdef BUILD_TEST - Guarded PROCESS_TYPE_CLIENT enum value with #ifdef BUILD_TEST - Guarded SimulationClient member in Process union with #ifdef BUILD_TEST - Guarded is_client() function with #ifdef BUILD_TEST - Guarded client detection logic in spawn_simulated_process() - Guarded PROCESS_TYPE_CLIENT cases in all switch statements: - spawn_simulated_process() init switch - free_process() cleanup switch - update_simulation() step switch **Result:** - tinydfs_server.out now compiles successfully - example_client.out now compiles successfully - tinydfs_test.out continues to work correctly - Simulation client code only included when BUILD_TEST is defined - All three build targets pass compilation with only pre-existing warnings The simulation_client code is now properly isolated to test builds only, preventing linker errors when building the real server and example client. --- src/main_server.c | 10 ++++++---- src/system.c | 21 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main_server.c b/src/main_server.c index ddd9f6f..8ffea16 100644 --- a/src/main_server.c +++ b/src/main_server.c @@ -18,14 +18,15 @@ int metadata_server_main(int argc, char **argv) void *contexts[MAX_CONNS+1]; struct pollfd polled[MAX_CONNS+1]; int num_polled; + int timeout; MetadataServer state; num_polled = metadata_server_init( - &state, argc, argv, contexts, polled); + &state, argc, argv, contexts, polled, &timeout); if (num_polled < 0) return -1; for (;;) { POLL(polled, num_polled, -1); num_polled = metadata_server_step( - &state, contexts, polled, num_polled); + &state, contexts, polled, num_polled, &timeout); if (num_polled < 0) return -1; } metadata_server_free(&state); @@ -37,14 +38,15 @@ int chunk_server_main(int argc, char **argv) void *contexts[MAX_CONNS+1]; struct pollfd polled[MAX_CONNS+1]; int num_polled; + int timeout; ChunkServer state; num_polled = chunk_server_init( - &state, argc, argv, contexts, polled); + &state, argc, argv, contexts, polled, &timeout); if (num_polled < 0) return -1; for (;;) { POLL(polled, num_polled, -1); num_polled = chunk_server_step( - &state, contexts, polled, num_polled); + &state, contexts, polled, num_polled, &timeout); if (num_polled < 0) return -1; } chunk_server_free(&state); diff --git a/src/system.c b/src/system.c index 6a88aec..ff87de2 100644 --- a/src/system.c +++ b/src/system.c @@ -5,7 +5,9 @@ #include "system.h" #include "chunk_server.h" #include "metadata_server.h" +#ifdef BUILD_TEST #include "simulation_client.h" +#endif #ifdef _WIN32 #define NATIVE_HANDLE HANDLE @@ -135,7 +137,9 @@ typedef struct { typedef enum { PROCESS_TYPE_METADATA_SERVER, PROCESS_TYPE_CHUNK_SERVER, +#ifdef BUILD_TEST PROCESS_TYPE_CLIENT, +#endif } ProcessType; struct Process { @@ -152,7 +156,9 @@ struct Process { union { ChunkServer chunk_server; MetadataServer metadata_server; +#ifdef BUILD_TEST SimulationClient simulation_client; +#endif }; }; @@ -244,6 +250,7 @@ static bool is_leader(int argc, char **argv) return false; } +#ifdef BUILD_TEST static bool is_client(int argc, char **argv) { for (int i = 0; i < argc; i++) @@ -251,6 +258,7 @@ static bool is_client(int argc, char **argv) return true; return false; } +#endif #define MAX_ARGS 128 @@ -299,16 +307,21 @@ int spawn_simulated_process(char *args) } bool leader = is_leader(argc, argv); +#ifdef BUILD_TEST bool client = is_client(argc, argv); +#endif Process *process = malloc(sizeof(Process)); if (process == NULL) return -1; // Determine process type +#ifdef BUILD_TEST if (client) { process->type = PROCESS_TYPE_CLIENT; - } else if (leader) { + } else +#endif + if (leader) { process->type = PROCESS_TYPE_METADATA_SERVER; } else { process->type = PROCESS_TYPE_CHUNK_SERVER; @@ -334,9 +347,11 @@ int spawn_simulated_process(char *args) case PROCESS_TYPE_CHUNK_SERVER: num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled, &timeout); break; +#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: num_polled = simulation_client_init(&process->simulation_client, argc, argv, contexts, polled, &timeout); break; +#endif default: num_polled = -1; break; @@ -368,9 +383,11 @@ static void free_process(Process *process) case PROCESS_TYPE_CHUNK_SERVER: chunk_server_free(&process->chunk_server); break; +#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: simulation_client_free(&process->simulation_client); break; +#endif } free(process); } @@ -553,9 +570,11 @@ void update_simulation(void) case PROCESS_TYPE_CHUNK_SERVER: num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled, &timeout); break; +#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: num_polled = simulation_client_step(¤t_process->simulation_client, contexts, polled, num_polled, &timeout); break; +#endif } if (num_polled < 0) { From 0d3edf9155a8c82a51d9000231843e57676a1403 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Nov 2025 13:07:06 +0000 Subject: [PATCH 4/5] Initialize timeout parameter to -1 in main_server.c Fixed uninitialized timeout variables in metadata_server_main() and chunk_server_main() functions by setting default value to -1. **Changes:** - metadata_server_main(): Changed `int timeout;` to `int timeout = -1;` - chunk_server_main(): Changed `int timeout;` to `int timeout = -1;` This ensures the timeout parameter has a defined default value before being passed to init functions, which is important for predictable behavior even though the init functions set the value themselves. All build targets (tinydfs_server.out, example_client.out, tinydfs_test.out) compile and run successfully. --- src/main_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main_server.c b/src/main_server.c index 8ffea16..2f8fcba 100644 --- a/src/main_server.c +++ b/src/main_server.c @@ -18,7 +18,7 @@ int metadata_server_main(int argc, char **argv) void *contexts[MAX_CONNS+1]; struct pollfd polled[MAX_CONNS+1]; int num_polled; - int timeout; + int timeout = -1; MetadataServer state; num_polled = metadata_server_init( &state, argc, argv, contexts, polled, &timeout); @@ -38,7 +38,7 @@ int chunk_server_main(int argc, char **argv) void *contexts[MAX_CONNS+1]; struct pollfd polled[MAX_CONNS+1]; int num_polled; - int timeout; + int timeout = -1; ChunkServer state; num_polled = chunk_server_init( &state, argc, argv, contexts, polled, &timeout); From 74b259ef196078ec3c3109993ce1c19dbd7a23d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Nov 2025 13:10:17 +0000 Subject: [PATCH 5/5] Wrap entire system.c with BUILD_TEST guard instead of nested conditionals Simplified conditional compilation by wrapping the entire system.c file with a single #ifdef BUILD_TEST guard, removing all nested conditionals for simulation client code. **Rationale:** The entire system.c file contains simulation framework code (mock functions, Process structure, spawn_simulated_process, update_simulation, etc.) that is only used when BUILD_TEST is defined. Having nested #ifdef BUILD_TEST conditionals within this file made no sense. **Changes:** **system.c:** - Added #ifdef BUILD_TEST at the very beginning of the file (line 1) - Added #endif // BUILD_TEST at the very end of the file (line 1486) - Removed #ifdef BUILD_TEST around simulation_client.h include - Removed #ifdef BUILD_TEST around PROCESS_TYPE_CLIENT enum value - Removed #ifdef BUILD_TEST around SimulationClient union member - Removed #ifdef BUILD_TEST around is_client() function - Removed #ifdef BUILD_TEST around client detection in spawn_simulated_process() - Removed #ifdef BUILD_TEST around PROCESS_TYPE_CLIENT cases in: - spawn_simulated_process() init switch - free_process() cleanup switch - update_simulation() step switch **Result:** - Much cleaner code with single top-level BUILD_TEST guard - No nested conditionals cluttering the code - All three build targets still compile successfully: - tinydfs_server.out (excludes system.c entirely) - example_client.out (excludes system.c entirely) - tinydfs_test.out (includes all of system.c) - Simulation runs correctly with client support The file structure now correctly reflects that system.c is purely test infrastructure and not used in production builds. --- src/system.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/system.c b/src/system.c index ff87de2..e7fa456 100644 --- a/src/system.c +++ b/src/system.c @@ -1,3 +1,5 @@ +#ifdef BUILD_TEST + #include #include #include @@ -5,9 +7,7 @@ #include "system.h" #include "chunk_server.h" #include "metadata_server.h" -#ifdef BUILD_TEST #include "simulation_client.h" -#endif #ifdef _WIN32 #define NATIVE_HANDLE HANDLE @@ -137,9 +137,7 @@ typedef struct { typedef enum { PROCESS_TYPE_METADATA_SERVER, PROCESS_TYPE_CHUNK_SERVER, -#ifdef BUILD_TEST PROCESS_TYPE_CLIENT, -#endif } ProcessType; struct Process { @@ -156,9 +154,7 @@ struct Process { union { ChunkServer chunk_server; MetadataServer metadata_server; -#ifdef BUILD_TEST SimulationClient simulation_client; -#endif }; }; @@ -250,7 +246,6 @@ static bool is_leader(int argc, char **argv) return false; } -#ifdef BUILD_TEST static bool is_client(int argc, char **argv) { for (int i = 0; i < argc; i++) @@ -258,7 +253,6 @@ static bool is_client(int argc, char **argv) return true; return false; } -#endif #define MAX_ARGS 128 @@ -307,21 +301,16 @@ int spawn_simulated_process(char *args) } bool leader = is_leader(argc, argv); -#ifdef BUILD_TEST bool client = is_client(argc, argv); -#endif Process *process = malloc(sizeof(Process)); if (process == NULL) return -1; // Determine process type -#ifdef BUILD_TEST if (client) { process->type = PROCESS_TYPE_CLIENT; - } else -#endif - if (leader) { + } else if (leader) { process->type = PROCESS_TYPE_METADATA_SERVER; } else { process->type = PROCESS_TYPE_CHUNK_SERVER; @@ -347,11 +336,9 @@ int spawn_simulated_process(char *args) case PROCESS_TYPE_CHUNK_SERVER: num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled, &timeout); break; -#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: num_polled = simulation_client_init(&process->simulation_client, argc, argv, contexts, polled, &timeout); break; -#endif default: num_polled = -1; break; @@ -383,11 +370,9 @@ static void free_process(Process *process) case PROCESS_TYPE_CHUNK_SERVER: chunk_server_free(&process->chunk_server); break; -#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: simulation_client_free(&process->simulation_client); break; -#endif } free(process); } @@ -570,11 +555,9 @@ void update_simulation(void) case PROCESS_TYPE_CHUNK_SERVER: num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled, &timeout); break; -#ifdef BUILD_TEST case PROCESS_TYPE_CLIENT: num_polled = simulation_client_step(¤t_process->simulation_client, contexts, polled, num_polled, &timeout); break; -#endif } if (num_polled < 0) { @@ -1481,4 +1464,6 @@ int mock_mkdir(char *path, mode_t mode) return mkdir(path, mode); } -#endif +#endif // !_WIN32 + +#endif // BUILD_TEST