From 044291f2d9a1ad1001e7efdff0ab4f96e0de70e4 Mon Sep 17 00:00:00 2001 From: Matthew Hambrecht Date: Sat, 29 Mar 2025 16:08:35 -0400 Subject: [PATCH] quality of life updates * simplified office initialization * added ability to more easily add new mailboxes * users can more easily retrieve a vacant mailbox id --- README.md | 35 +++++++++---- mailbox.h | 45 +++++++++------- sample/reciever.c | 3 +- sample/sender.c | 3 +- tests/test.c | 130 +++++++++++++++++++++++++++++++++------------- 5 files changed, 149 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 511c9ec..c55065a 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,22 @@ To include the library add it to your source directory and add the header to you ### Basic Functions To setup a Mailbox we need a local "post office". To create a post office we use the office initializer. ```c -struct office * init_office(char * key, const unsigned int box_id); +struct office * init_office(char * key); ``` -The `key` is going to be the same amongst all programs and is how each process designates which memory store to share. The `box_id` you can think of as the unique post address for each program. It is how the programs know where to send to, and where they recieved a message from. +The `key` is going to be the same amongst all programs and is how each process designates which memory store to share. We can think of this as our mailing hub where all mail is routed through. All processes that wish to use the shared memory will need to connect to the office prior to doing anything else. -To send a message we need the "post address" of the program we wish to send to, a "post office" which was initialized in the prior step, and the actual "mail". +The `box_id` you can think of as the unique post address for each program. It is how the programs know where to send to, and/or allows you to depict where they recieved a message from. To create a mailbox you will use the following function: +```c +int add_mailbox(struct office * of, const unsigned int box_id); +``` +Processes are capable of sharing the same `box_id`, or even having multiple. It is up to the user to safely handle this to ensure messaging runs smoothly amongst processes, threads, etc. + +To quickly get the `box_id` of the next available mailbox there is the provided function: +```c +int get_vacant(struct office * of); +``` + +To send a message we need the "post address" of the program we wish to send to, our "post office" which was initialized in the prior step, and the actual "mail". ```c int send_mail(struct office * of, const unsigned int box_id, M * mail); ``` @@ -63,7 +74,8 @@ M * await_mail(struct office * of, const unsigned int box_id); Having the ability to wait for mail without knowing when it will arrive allows us to create a server-client relationship. A simple server would look as follows: ```c -office * of; +office * of = init_office("key"); +add_mailbox(&of, 0); while (1) { M * msg = await_mail(&of, 0); @@ -77,7 +89,7 @@ while (1) { While a client would be able to simply message the server at any point with: ```c M * msg; -office * of; +office * of = init_office("key"); send_mail(*of, 0, msg); ``` @@ -90,9 +102,10 @@ To see some samples of this library working in action, check `sample/`. | Code | Int | Description | |--------------|-----|--------------------------------------------------------------| | MB_SUCCESS | 0 | The intended task completed successfully. -| MB_EMPTY | 10 | The mailbox the process attempted to retrieve from is empty. | -| MB_FULL | 11 | The mailbox the process attempted to send to is full. | -| MB_NOT_FOUND | 12 | The mailbox with the provided ID doesn't exist. | -| MB_MEM_ERR | 13 | An error occurred while setting up the Shared Memory. | -| MB_BAD_ID | 14 | The provided ID is out of the allowed range. | -| MB_EEXIST | 15 | The mailbox the process attempted to create already exists. | \ No newline at end of file +| MB_EMPTY | -10 | The mailbox the process attempted to retrieve from is empty. | +| MB_FULL | -11 | The mailbox the process attempted to send to is full. | +| MB_NOT_FOUND | -12 | The mailbox with the provided ID doesn't exist. | +| MB_MEM_ERR | -13 | An error occurred while setting up the Shared Memory. | +| MB_BAD_ID | -14 | The provided ID is out of the allowed range. | +| MB_EEXIST | -15 | The mailbox the process attempted to create already exists. | +| MB_OFFICE_FULL | -16 | We have reached the maximum number of allowed mailboxes | \ No newline at end of file diff --git a/mailbox.h b/mailbox.h index 82dfc31..8620f93 100644 --- a/mailbox.h +++ b/mailbox.h @@ -21,12 +21,13 @@ Documentation: https://github.com/matthambrecht/Mailbox/blob/main/README.md // Error codes #define MB_SUCCESS 0 -#define MB_EMPTY 10 -#define MB_FULL 11 -#define MB_NOT_FOUND 12 -#define MB_MEM_ERR 13 -#define MB_BAD_ID 14 -#define MB_EEXIST 15 +#define MB_EMPTY -10 +#define MB_FULL -11 +#define MB_NOT_FOUND -12 +#define MB_MEM_ERR -13 +#define MB_BAD_ID -14 +#define MB_EEXIST -15 +#define MB_OFFICE_FULL -16 // Config consts #define MAX_MB 24 // Max number of processes capable of having a mailbox @@ -35,7 +36,7 @@ Documentation: https://github.com/matthambrecht/Mailbox/blob/main/README.md typedef struct { char msg_buff[256]; size_t msg_len; -} M; +} M; // Our distribution center types struct mailbox { @@ -46,7 +47,7 @@ struct mailbox { }; struct office { - char * key; + const char * key; unsigned int vacant[MAX_MB]; sem_t mutex[MAX_MB]; sem_t notif[MAX_MB]; @@ -66,8 +67,8 @@ int mb_full(struct mailbox * mb); int mb_empty(struct mailbox * mb); // office fn declarations -struct office * connect(char * key, const unsigned int box_id); -struct office * init_office(char * key, const unsigned int box_id); +struct office * connect(const char * key); +struct office * init_office(const char * key); void destroy_office(const char * key); int remove_mailbox(struct office * of, const unsigned int box_id); int add_mailbox(struct office * of, const unsigned int box_id); @@ -133,8 +134,8 @@ int mb_full(struct mailbox * mb) { // Office state stuff -struct office * connect(char * key, const unsigned int box_id) { // connect to existing - unsigned int fd; +struct office * connect(const char * key) { // connect to existing + int fd; struct office * of; fd = shm_open( @@ -154,14 +155,13 @@ struct office * connect(char * key, const unsigned int box_id) { // connect to e ); check_perr(fd == -1); - add_mailbox(of, box_id); return of; } -struct office * init_office(char * key, const unsigned int box_id) { - unsigned int fd; +struct office * init_office(const char * key) { + int fd; struct office * of; fd = shm_open( @@ -172,7 +172,7 @@ struct office * init_office(char * key, const unsigned int box_id) { if (fd == -1) { if (errno == EEXIST) { // office exists - return connect(key, box_id); + return connect(key); } check_perr(-1); @@ -198,8 +198,6 @@ struct office * init_office(char * key, const unsigned int box_id) { sem_init(&of->mutex[i], 1, 1); of->vacant[i] = 1; } - - add_mailbox(of, box_id); return of; } @@ -209,6 +207,15 @@ void destroy_office(const char * key) { shm_unlink(key); } +int get_vacant(struct office * of) { + for (size_t i = 0; i < MAX_MB; i++) { + if (of->vacant[i]) { + return i; + } + } + + return MB_OFFICE_FULL; +} int remove_mailbox(struct office * of, const unsigned int box_id) { if (box_id >= MAX_MB) { @@ -224,7 +231,7 @@ int remove_mailbox(struct office * of, const unsigned int box_id) { of->procs--; of->vacant[box_id] = 1; - if (of->procs) { + if (!of->procs) { destroy_office(of->key); } diff --git a/sample/reciever.c b/sample/reciever.c index 9987feb..b348d19 100644 --- a/sample/reciever.c +++ b/sample/reciever.c @@ -3,7 +3,8 @@ #include "../mailbox.h" int main() { - struct office *of = init_office("sample_mb", 1); + struct office *of = init_office("sample_mb"); + add_mailbox(of, 1); for (int i = 0; i < MAX_Q_LEN; i++) { M * o = await_mail(of, 1); diff --git a/sample/sender.c b/sample/sender.c index bb3c6df..7792c95 100644 --- a/sample/sender.c +++ b/sample/sender.c @@ -3,7 +3,8 @@ #include "../mailbox.h" int main() { - struct office *of = init_office("sample_mb", 3); + struct office *of = init_office("sample_mb"); + add_mailbox(of, 3); for (int i = 0; i < MAX_Q_LEN; i++) { char msg[256]; diff --git a/tests/test.c b/tests/test.c index 551e0c5..c85074b 100644 --- a/tests/test.c +++ b/tests/test.c @@ -20,6 +20,9 @@ int test_send(struct office * of, const int dest); // Test declarations void init_normal_tests(); +void add_normal_tests(); +void add_edge_tests(); +void add_error_tests(); void send_normal_tests(); void send_edge_tests(); void send_error_tests(); @@ -29,6 +32,8 @@ void check_error_tests(); void remove_normal_tests(); void remove_edge_tests(); void remove_error_tests(); +void get_vacant_normal_tests(); +void get_vacant_error_tests(); void destroy_normal_tests(); void destroy_edge_tests(); @@ -52,121 +57,167 @@ int test_send(struct office * of, const int dest) { } void init_normal_tests() { - struct office * of1 = init_office("test_of", 0); - struct office * of2 = init_office("test_of", 1); + struct office * of = init_office("test_of"); pass(__func__); } +void add_normal_tests() { + struct office * of = init_office("test_of"); + + // Add needed mailboxes + c_assert(__func__, add_mailbox(of, 0) == MB_SUCCESS); + c_assert(__func__, add_mailbox(of, 1) == MB_SUCCESS); + c_assert(__func__, add_mailbox(of, 3) == MB_SUCCESS); + + pass(__func__); +} + +void add_edge_tests() { + struct office * of = init_office("test_of"); + + // Readd Mailbox + c_assert(__func__, add_mailbox(of, 0) == MB_EEXIST); + + pass(__func__); +} + +void add_error_tests() { + struct office * of = init_office("test_of"); + + c_assert(__func__, add_mailbox(of, -1) == MB_BAD_ID); // OOB ID + + pass(__func__); +} + + void send_normal_tests() { - struct office * of1 = init_office("test_of", 0); - struct office * of2 = init_office("test_of", 1); + struct office * of = init_office("test_of"); // Send somewhere else - c_assert(__func__, test_send(of1, 1) == MB_SUCCESS); + c_assert(__func__, test_send(of, 1) == MB_SUCCESS); pass(__func__); } void send_edge_tests() { - struct office * of1 = init_office("test_of", 0); + struct office * of = init_office("test_of"); // Send self - c_assert(__func__, test_send(of1, 0) == MB_SUCCESS); + c_assert(__func__, test_send(of, 0) == MB_SUCCESS); for (int i = 0; i < MAX_Q_LEN; i++) { // Fill up mailbox - test_send(of1, 1); + test_send(of, 1); } // Send to full mailbox - c_assert(__func__, test_send(of1, 1) == MB_FULL); + c_assert(__func__, test_send(of, 1) == MB_FULL); pass(__func__); } void send_error_tests() { - struct office * of1 = init_office("test_of", 0); - struct office * of2 = init_office("test_of", 1); + struct office * of = init_office("test_of"); - c_assert(__func__, test_send(of1, -1) == MB_BAD_ID); // OOB ID - c_assert(__func__, test_send(of1, 2) == MB_NOT_FOUND); // Nonexistent ID + c_assert(__func__, test_send(of, -1) == MB_BAD_ID); // OOB ID + c_assert(__func__, test_send(of, 2) == MB_NOT_FOUND); // Nonexistent ID pass(__func__); } void check_normal_tests() { - struct office * of1 = init_office("test_of", 0); - struct office * of2 = init_office("test_of", 1); + struct office * of = init_office("test_of"); // Check mailbox with atleast something - c_assert(__func__, check_mail(of1, 1) != NULL); + c_assert(__func__, check_mail(of, 1) != NULL); for (int i = 0; i < MAX_Q_LEN; i++) { // Empty mailbox - check_mail(of1, 1); + check_mail(of, 1); } - c_assert(__func__, check_mail(of1, 1) == NULL); + c_assert(__func__, check_mail(of, 1) == NULL); pass(__func__); } void check_edge_tests() { - struct office * of1 = init_office("test_of", 3); + struct office * of = init_office("test_of"); - c_assert(__func__, check_mail(of1, 3) == NULL); + c_assert(__func__, check_mail(of, 3) == NULL); pass(__func__); } void check_error_tests() { - struct office * of1 = init_office("test_of", 0); - struct office * of2 = init_office("test_of", 1); + struct office * of = init_office("test_of"); - c_assert(__func__, check_mail(of1, -1) == NULL); // OOB ID - c_assert(__func__, check_mail(of1, 2) == NULL); // Nonexistent ID + c_assert(__func__, check_mail(of, -1) == NULL); // OOB ID + c_assert(__func__, check_mail(of, 2) == NULL); // Nonexistent ID pass(__func__); } void remove_normal_tests() { - struct office * of1 = init_office("test_of", 1); + struct office * of = init_office("test_of"); // Remove existent mailbox - c_assert(__func__, remove_mailbox(of1, 1) == MB_SUCCESS); + c_assert(__func__, remove_mailbox(of, 1) == MB_SUCCESS); pass(__func__); } void remove_edge_tests() { - struct office * of1 = init_office("test_of", 3); + struct office * of = init_office("test_of"); // Double delete test - c_assert(__func__, remove_mailbox(of1, 3) == MB_SUCCESS); - c_assert(__func__, remove_mailbox(of1, 3) == MB_NOT_FOUND); + c_assert(__func__, remove_mailbox(of, 3) == MB_SUCCESS); + c_assert(__func__, remove_mailbox(of, 3) == MB_NOT_FOUND); pass(__func__); } void remove_error_tests() { - struct office * of1 = init_office("test_of", 0); + struct office * of = init_office("test_of"); - c_assert(__func__, remove_mailbox(of1, -1) == MB_BAD_ID); // OOB ID - c_assert(__func__, remove_mailbox(of1, 2) == MB_NOT_FOUND); // Nonexistent ID + c_assert(__func__, remove_mailbox(of, -1) == MB_BAD_ID); // OOB ID + c_assert(__func__, remove_mailbox(of, 2) == MB_NOT_FOUND); // Nonexistent ID + + pass(__func__); +} + +void get_vacant_normal_tests() { + struct office * of = init_office("test_of"); + + // Check first known vacant box + c_assert(__func__, get_vacant(of) == 2); + + pass(__func__); +} + +void get_vacant_error_tests() { + struct office * of = init_office("test_of"); + + // Fill office + for (int i = 0; i < MAX_MB; i++) { + add_mailbox(of, i); + } + + c_assert(__func__, get_vacant(of) == MB_OFFICE_FULL); pass(__func__); } void destroy_normal_tests() { - struct office * of1 = init_office("test_of", 0); + struct office * of = init_office("test_of"); - destroy_office(of1->key); + destroy_office(of->key); pass(__func__); } void destroy_edge_tests() { - struct office * of1 = init_office("test_of", 0); + struct office * of = init_office("test_of"); destroy_office("test_of"); destroy_office("test_of"); @@ -178,6 +229,11 @@ int main() { // Init tests init_normal_tests(); + // Add mailbox tests + add_normal_tests(); + add_edge_tests(); + add_error_tests(); + // Send tests send_normal_tests(); send_edge_tests(); @@ -188,11 +244,15 @@ int main() { check_edge_tests(); check_error_tests(); + get_vacant_normal_tests(); // Spread out for state reasons + // Remove tests remove_normal_tests(); remove_edge_tests(); remove_error_tests(); + get_vacant_error_tests(); + // Destroy Tests destroy_normal_tests(); destroy_edge_tests();