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/main_server.c b/src/main_server.c index ddd9f6f..2f8fcba 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 = -1; 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 = -1; 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/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/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/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..e7fa456 100644 --- a/src/system.c +++ b/src/system.c @@ -1,3 +1,5 @@ +#ifdef BUILD_TEST + #include #include #include @@ -5,6 +7,7 @@ #include "system.h" #include "chunk_server.h" #include "metadata_server.h" +#include "simulation_client.h" #ifdef _WIN32 #define NATIVE_HANDLE HANDLE @@ -131,6 +134,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 +150,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 +163,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 +246,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 +301,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; @@ -290,18 +325,30 @@ 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; - 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, &timeout); + break; + case PROCESS_TYPE_CHUNK_SERVER: + num_polled = chunk_server_init(&process->chunk_server, argc, argv, contexts, polled, &timeout); + break; + case PROCESS_TYPE_CLIENT: + num_polled = simulation_client_init(&process->simulation_client, argc, argv, contexts, polled, &timeout); + break; + default: + num_polled = -1; + break; } + current_process = NULL; if (num_polled < 0) { // TODO } + if (timeout < 0) { process->wakeup_time = INVALID_TIME; } else { @@ -311,22 +358,32 @@ int spawn_simulated_process(char *args) 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) @@ -491,10 +548,16 @@ 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, &timeout); + break; + case PROCESS_TYPE_CHUNK_SERVER: + num_polled = chunk_server_step(¤t_process->chunk_server, contexts, polled, num_polled, &timeout); + break; + case PROCESS_TYPE_CLIENT: + num_polled = simulation_client_step(¤t_process->simulation_client, contexts, polled, num_polled, &timeout); + break; } if (num_polled < 0) { @@ -1401,4 +1464,6 @@ int mock_mkdir(char *path, mode_t mode) return mkdir(path, mode); } -#endif +#endif // !_WIN32 + +#endif // BUILD_TEST 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);