Compare commits

..
12 Commits
Author SHA1 Message Date
danemacfadden 0b6aa705ee Test gitea push 2 2026-11-06 21:10:03 -06:00
danemacfadden 827360f899 Test gitea push 2026-11-06 21:08:41 -06:00
danemacfadden 6de7b5b67e Added comments and removed unnecessary code 2026-10-19 21:55:32 -06:00
danemacfadden 0c825800b3 Changed context declaration to properly use pointers for mutexes and other members 2026-10-19 21:52:54 -06:00
danemacfadden 863c6e28d0 Removed AI slop logging test 2026-10-19 21:07:48 -06:00
danemacfadden 370a1b7f80 Protected modifications of nc with render mutex to avoid messages printing in the incorrect order 2026-10-18 13:50:37 -06:00
danemacfadden 190cd7feac Fixed a memory leak in the output thread function by moving the msg malloc after where the thread waits for something to print. If the thread gets cancelled while waiting on this cv, msg never gets properly freed. 2026-10-12 20:20:50 -06:00
danemacfadden 6a430b5dba added and initialized mutex for saved message list 2026-09-30 20:19:51 -06:00
danemacfadden 83e54e2229 Removed output.txt 2026-09-26 13:31:56 -06:00
danemacfadden 78ef3f1015 Remove .o files from remote tracking 2026-09-26 13:31:25 -06:00
danemacfadden 5d7e2baebd merge fix 2026-09-26 13:22:20 -06:00
danemacfadden cc793e134a Added list to store history of messages. This is done to address a memory leak. Each thread allocates another 512 bytes and never frees it, but the program breaks if it is freed. Storing a copy of the messages in this list will allow each message stored to be trimmed to its actual size and not 512 bytes, and then the original message can be freed. At the end of execution, the list can be freed. 2026-09-26 13:22:20 -06:00
3 changed files with 135 additions and 84 deletions
+17
View File
@@ -0,0 +1,17 @@
/* threading stuff */
LIST *receivelist, *sendlist;
pthread_t input_thread, send_thread, receive_thread, output_thread;
pthread_mutex_t sendmut, receivemut, sendcvmut, receivecvmut, rendermut;
pthread_cond_t sendcv, receivecv;
char closed[] = "Connection closed.";
/* networking stuff */
int confd, sockfd;
char s[INET6_ADDRSTRLEN];
/* notcurses stuff */
struct notcurses *nc;
struct ncplane *chatlog, *prompt, *stdn;
unsigned int rows, cols;
-1
View File
@@ -31,7 +31,6 @@ int ListRemove(LIST *list) {
list->current = list->current->next; list->current = list->current->next;
} }
unused->data = NULL;
free(unused->data); free(unused->data);
free(unused); free(unused);
list->size--; list->size--;
+100 -65
View File
@@ -37,27 +37,30 @@ void *input(void *arg) {
msg = malloc(512); msg = malloc(512);
memset(msg, 0, 512); memset(msg, 0, 512);
i = 0; i = 0;
ncplane_printf_yx(context->plane_prompt, 0, 0, "Type a message: ");
pthread_mutex_lock(&context->mut_render); pthread_mutex_lock(context->mut_render);
ncplane_printf_yx(context->plane_prompt, 0, 0, "Type a message: ");
notcurses_render(context->ctx_nc); notcurses_render(context->ctx_nc);
pthread_mutex_unlock(&context->mut_render); pthread_mutex_unlock(context->mut_render);
/* Loop to get input */ /* Loop to get input */
while (1) { while (1) {
character = notcurses_get(context->ctx_nc, NULL, &ni); character = notcurses_get(context->ctx_nc, NULL, &ni);
if (character == NCKEY_ENTER) { if (character == NCKEY_ENTER) {
/* Add to sendlist when user presses enter */ /* Add to sendlist when user presses enter */
pthread_mutex_lock(&context->mut_send); pthread_mutex_lock(context->mut_send);
ListAppend(context->ctx_sendlist, msg); ListAppend(context->ctx_sendlist, msg);
pthread_mutex_unlock(&context->mut_send); pthread_mutex_unlock(context->mut_send);
pthread_mutex_lock(context->mut_render);
ncplane_erase_region(context->plane_prompt, 0, 16, 1, ncplane_erase_region(context->plane_prompt, 0, 16, 1,
context->ctx_cols); context->ctx_cols);
pthread_mutex_unlock(context->mut_render);
pthread_mutex_lock(&context->mut_render); pthread_mutex_lock(context->mut_render);
notcurses_render(context->ctx_nc); notcurses_render(context->ctx_nc);
pthread_mutex_unlock(&context->mut_render); pthread_mutex_unlock(context->mut_render);
break; break;
} }
@@ -71,18 +74,23 @@ void *input(void *arg) {
} }
/* Print the message as the user types it */ /* Print the message as the user types it */
pthread_mutex_lock(context->mut_render);
ncplane_erase_region(context->plane_prompt, 0, 0, 1, context->ctx_cols); ncplane_erase_region(context->plane_prompt, 0, 0, 1, context->ctx_cols);
ncplane_printf_yx(context->plane_prompt, 0, 0, "Type a message: %s", msg); ncplane_printf_yx(context->plane_prompt, 0, 0, "Type a message: %s", msg);
pthread_mutex_lock(&context->mut_render);
notcurses_render(context->ctx_nc); notcurses_render(context->ctx_nc);
pthread_mutex_unlock(&context->mut_render); pthread_mutex_unlock(context->mut_render);
} }
/* Wake up thread if it was waiting to have a msg to send */ /* Wake up thread if it was waiting to have a msg to send */
pthread_mutex_lock(context->mut_sendcv);
if (ListCount(context->ctx_sendlist) == 1) { if (ListCount(context->ctx_sendlist) == 1) {
pthread_cond_signal(&context->cv_send); pthread_cond_signal(context->cv_send);
} }
pthread_mutex_unlock(context->mut_sendcv);
/* s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");*/
} }
return 0; return 0;
} }
@@ -94,13 +102,18 @@ void *sender(void *arg) {
CONTEXT *context = (CONTEXT *)arg; CONTEXT *context = (CONTEXT *)arg;
while (1) { while (1) {
/*
s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");
*/
/* Check if the list has any content to send /* Check if the list has any content to send
* If not, wait for some to arrive */ * If not, wait for some to arrive */
pthread_mutex_lock(context->mut_sendcv);
if (ListCount(context->ctx_sendlist) == 0) { if (ListCount(context->ctx_sendlist) == 0) {
pthread_mutex_lock(&context->mut_sendcv); pthread_cond_wait(context->cv_send, context->mut_sendcv);
pthread_cond_wait(&context->cv_send, &context->mut_sendcv);
pthread_mutex_unlock(&context->mut_sendcv);
} }
pthread_mutex_unlock(context->mut_sendcv);
msg = malloc(512); msg = malloc(512);
header = malloc(519); header = malloc(519);
@@ -110,11 +123,11 @@ void *sender(void *arg) {
/* Obtain mutex, get and then remove message /* Obtain mutex, get and then remove message
* from the list, unlock mutex, * from the list, unlock mutex,
* send message. */ * send message. */
pthread_mutex_unlock(&context->mut_send); pthread_mutex_lock(context->mut_send);
ListFirst(context->ctx_sendlist); ListFirst(context->ctx_sendlist);
strcpy(msg, (char*)ListCurr(context->ctx_sendlist)); strcpy(msg, (char*)ListCurr(context->ctx_sendlist));
ListRemove(context->ctx_sendlist); ListRemove(context->ctx_sendlist);
pthread_mutex_unlock(&context->mut_send); pthread_mutex_unlock(context->mut_send);
bytes = send(context->ctx_confd, msg, 512, 0); bytes = send(context->ctx_confd, msg, 512, 0);
@@ -123,21 +136,28 @@ void *sender(void *arg) {
pthread_cancel(*context->thread_in); pthread_cancel(*context->thread_in);
pthread_cancel(*context->thread_out); pthread_cancel(*context->thread_out);
pthread_cancel(*context->thread_recv); pthread_cancel(*context->thread_recv);
pthread_exit(&context->ctx_closed); pthread_exit(context->ctx_closed);
} }
/* Append message to header and add to print list */ /* Append message to header and add to print list */
strcpy(header, "You: "); strcpy(header, "You: ");
strncat(header, msg, strlen(msg)); strncat(header, msg, strlen(msg));
pthread_mutex_lock(&context->mut_recv); /* Add to receive list. This is done to print the received and
* sent messages in order */
pthread_mutex_lock(context->mut_recv);
ListAppend(context->ctx_recvlist, header); ListAppend(context->ctx_recvlist, header);
pthread_mutex_unlock(&context->mut_recv); pthread_mutex_unlock(context->mut_recv);
/* Wake thread waiting for message to print */ /* Wake thread waiting for message to print */
if (ListCount(context->ctx_recvlist) == 1) { pthread_cond_signal(context->cv_recv);
pthread_cond_signal(&context->cv_recv);
} free(msg);
/*
s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");
*/
} }
return 0; return 0;
} }
@@ -150,9 +170,9 @@ void *receiver(void *arg) {
while (1) { while (1) {
msg = malloc(512); msg = malloc(512);
header = malloc(strlen(context->ip) + 514); header = malloc(strlen(context->ip) + 515);
memset(msg, 0, 512); memset(msg, 0, 512);
memset(header, 0, strlen(context->ip) + 514); memset(header, 0, strlen(context->ip) + 515);
bytes = recv(context->ctx_confd, msg, 512, 0); bytes = recv(context->ctx_confd, msg, 512, 0);
@@ -161,7 +181,7 @@ void *receiver(void *arg) {
pthread_cancel(*context->thread_send); pthread_cancel(*context->thread_send);
pthread_cancel(*context->thread_in); pthread_cancel(*context->thread_in);
pthread_cancel(*context->thread_out); pthread_cancel(*context->thread_out);
pthread_exit(&context->ctx_closed); pthread_exit(context->ctx_closed);
} }
strncpy(header, context->ip, strlen(context->ip)); strncpy(header, context->ip, strlen(context->ip));
@@ -170,14 +190,14 @@ void *receiver(void *arg) {
/* Add message to list for other thread to print */ /* Add message to list for other thread to print */
if (bytes != -1) { if (bytes != -1) {
pthread_mutex_lock(&context->mut_recv); pthread_mutex_lock(context->mut_recv);
ListAppend(context->ctx_recvlist, header); ListAppend(context->ctx_recvlist, header);
pthread_mutex_unlock(&context->mut_recv); pthread_mutex_unlock(context->mut_recv);
} }
/* Signal output thread, if it was waiting for something to print */ /* Signal output thread, if it was waiting for something to print */
if (ListCount(context->ctx_recvlist) == 1) { if (ListCount(context->ctx_recvlist) == 1) {
pthread_cond_signal(&context->cv_recv); pthread_cond_signal(context->cv_recv);
} }
} }
return 0; return 0;
@@ -189,27 +209,28 @@ void *output(void *arg) {
CONTEXT *context = (CONTEXT *)arg; CONTEXT *context = (CONTEXT *)arg;
while (1) { while (1) {
msg = malloc(512 + strlen(context->ip));
/* Block if there's nothing to output */ /* Block if there's nothing to output */
if (ListCount(context->ctx_recvlist) == 0) { if (ListCount(context->ctx_recvlist) == 0) {
pthread_mutex_lock(&context->mut_recvcv); pthread_mutex_lock(context->mut_recvcv);
pthread_cond_wait(&context->cv_recv, &context->mut_recvcv); pthread_cond_wait(context->cv_recv, context->mut_recvcv);
pthread_mutex_unlock(&context->mut_recvcv); pthread_mutex_unlock(context->mut_recvcv);
} }
msg = malloc(515 + strlen(context->ip));
/* Remove a message from the list and print it to chatlog */ /* Remove a message from the list and print it to chatlog */
pthread_mutex_lock(&context->mut_recv); pthread_mutex_lock(context->mut_recv);
ListFirst(context->ctx_recvlist); ListFirst(context->ctx_recvlist);
strcpy(msg, (char*)ListCurr(context->ctx_recvlist)); strcpy(msg, (char*)ListCurr(context->ctx_recvlist));
ListRemove(context->ctx_recvlist); ListRemove(context->ctx_recvlist);
pthread_mutex_unlock(&context->mut_recv); pthread_mutex_unlock(context->mut_recv);
pthread_mutex_lock(context->mut_render);
ncplane_printf(context->plane_log, "%s\n", msg); ncplane_printf(context->plane_log, "%s\n", msg);
pthread_mutex_lock(&context->mut_render);
notcurses_render(context->ctx_nc); notcurses_render(context->ctx_nc);
pthread_mutex_unlock(&context->mut_render); pthread_mutex_unlock(context->mut_render);
free(msg);
} }
return 0; return 0;
} }
@@ -236,9 +257,15 @@ int main(int argc, char* argv[]) {
int yes = 1; int yes = 1;
/* threading stuff */ /* threading stuff */
CONTEXT *context = malloc(sizeof(CONTEXT));
LIST *receivelist, *sendlist; LIST *receivelist, *sendlist;
pthread_t input_thread, send_thread, receive_thread, output_thread; pthread_t input_thread, send_thread, receive_thread, output_thread;
pthread_mutex_t sendmut, receivemut, sendcvmut, receivecvmut, rendermut; pthread_mutex_t sendmut,
receivemut,
sendcvmut,
receivecvmut,
rendermut,
savedmut;
pthread_cond_t sendcv, receivecv; pthread_cond_t sendcv, receivecv;
char closed[] = "Connection closed."; char closed[] = "Connection closed.";
@@ -389,6 +416,10 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "Error: render mutex init failed\n"); fprintf(stderr, "Error: render mutex init failed\n");
return -1; return -1;
} }
if (pthread_mutex_init(&savedmut, NULL) != 0) {
fprintf(stderr, "Error: saved mutex init failed\n");
return -1;
}
if (pthread_cond_init(&sendcv, NULL) != 0) { if (pthread_cond_init(&sendcv, NULL) != 0) {
fprintf(stderr, "Error: send CV init failed\n"); fprintf(stderr, "Error: send CV init failed\n");
return -1; return -1;
@@ -428,39 +459,43 @@ int main(int argc, char* argv[]) {
} }
ncplane_set_scrolling(chatlog, true); ncplane_set_scrolling(chatlog, true);
CONTEXT context = { /* Lists to store messages */
.ctx_recvlist = receivelist, context->ctx_recvlist = receivelist;
.ctx_sendlist = sendlist, context->ctx_sendlist = sendlist;
.thread_in = &input_thread, /* Threads */
.thread_out = &output_thread, context->thread_in = &input_thread;
.thread_recv = &receive_thread, context->thread_out = &output_thread;
.thread_send = &send_thread, context->thread_recv = &receive_thread;
context->thread_send = &send_thread;
.mut_send = sendmut, /* Mutexes */
.mut_recv = receivemut, context->mut_send = &sendmut;
.mut_sendcv = sendcvmut, context->mut_recv = &receivemut;
.mut_recvcv = receivecvmut, context->mut_sendcv = &sendcvmut;
.mut_render = rendermut, context->mut_recvcv = &receivecvmut;
context->mut_render = &rendermut;
.cv_send = sendcv, /* Condition variables */
.cv_recv = receivecv, context->cv_send = &sendcv;
context->cv_recv = &receivecv;
.ip = s, /* Networking stuff */
.ctx_confd = confd, context->ip = s;
context->ctx_confd = confd;
.ctx_nc = nc, /* Notcurses rendering stuff */
.plane_log = chatlog, context->ctx_nc = nc;
.plane_prompt = prompt, context->plane_log = chatlog;
.ctx_rows = rows, context->plane_prompt = prompt;
.ctx_cols = cols, context->ctx_rows = rows;
.ctx_closed = closed, context->ctx_cols = cols;
}; context->ctx_closed = closed;
pthread_create(&input_thread, NULL, input, &context); pthread_create(&input_thread, NULL, input, context);
pthread_create(&send_thread, NULL, sender, &context); pthread_create(&send_thread, NULL, sender, context);
pthread_create(&output_thread, NULL, output, &context); pthread_create(&output_thread, NULL, output, context);
pthread_create(&receive_thread, NULL, receiver, &context); pthread_create(&receive_thread, NULL, receiver, context);
pthread_join(input_thread, NULL); pthread_join(input_thread, NULL);
pthread_join(output_thread, NULL); pthread_join(output_thread, NULL);