diff --git a/randomstring.c b/randomstring.c new file mode 100644 index 0000000..3f65c50 --- /dev/null +++ b/randomstring.c @@ -0,0 +1,21 @@ +// returns symbols from 32 to 126 read from /dev/urandom, not thread-safe +// s - length of data with trailing zero + +#include +char *randstring(size_t *s){ + static char buf[256]; + char rbuf[255]; + ssize_t L = getrandom(rbuf, 255, 0); + if(L < 1){ + *s = 0; + return NULL; + } + size_t total = 0; + for(size_t x = 0; x < (size_t)L; ++x){ + char s = rbuf[x]; + if(s > 31 && s < 127) buf[total++] = s; + } + buf[total++] = 0; + *s = total; + return buf; +} diff --git a/thread_array_management/Makefile b/thread_array_management/Makefile new file mode 100644 index 0000000..642a93d --- /dev/null +++ b/thread_array_management/Makefile @@ -0,0 +1,43 @@ +# run `make DEF=...` to add extra defines +PROGRAM := array +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lreadline -lusefull_macros -pthread +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O2 -Wno-trampolines -std=gnu99 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +CC = gcc +#CXX = g++ + + +all : $(OBJDIR) $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror -Wall -Wextra +debug: all + +$(PROGRAM) : $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM) + +$(OBJDIR): + mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(OBJS) $(DEPS) + @rmdir $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/thread_array_management/Readme b/thread_array_management/Readme new file mode 100644 index 0000000..b801476 --- /dev/null +++ b/thread_array_management/Readme @@ -0,0 +1 @@ +Work with named threads list, communicate by FIFO messages diff --git a/thread_array_management/test.c b/thread_array_management/test.c new file mode 100644 index 0000000..2ec5ccf --- /dev/null +++ b/thread_array_management/test.c @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include + +// 3 include-files for readline +#include +#include +#include + +#define MAX_MSG_LEN (1023) + +typedef struct msglist_{ + char *data; + struct msglist_ *next, *last; +} msglist; + +typedef enum{ + idxMOSI = 0, + idxMISO = 1 +} msgidx; + +// index 0 - MOSI, index 1 - MISO +typedef struct{ + msglist *text[2]; // stringified text messages + pthread_mutex_t mutex[2]; // text changing mutex +} message; + +#define THREADNAMEMAXLEN (31) +typedef struct{ + char name[THREADNAMEMAXLEN+1]; + message mesg; + pthread_t thread; + void *(*handler)(void *); +} threadinfo; + +typedef struct thread_list_{ + threadinfo ti; + struct thread_list_ *next; +} threadlist; + +static threadlist *thelist = NULL; + +/** + * push data into the tail of a list (FIFO) + * @param lst (io) - list + * @param v (i) - data to push + * @return pointer to just pused node + */ +static msglist *pushmessage(msglist **lst, char *v){ + if(!lst || !v) return NULL; + msglist *node; + if((node = MALLOC(msglist, 1)) == 0) + return NULL; // allocation error + node->data = strdup(v); + if(!node->data){ + FREE(node); + return NULL; + } + if(!*lst){ + *lst = node; + (*lst)->last = node; + return node; + } + (*lst)->last->next = node; + (*lst)->last = node; + return node; +} + +/** + * @brief popmessage - get data from head of list + * @param lst (io) - list + * @return data from first node or NULL if absent (SHOULD BE FREEd AFER USAGE!) + */ +static char *popmessage(msglist **lst){ + if(!lst || !*lst) return NULL; + char *ret; + msglist *node = *lst; + ret = (*lst)->data; + *lst = (*lst)->next; + FREE(node); + return ret; +} + +static threadlist *getlast(){ + if(!thelist) return NULL; + threadlist *t = thelist; + while(t->next) t = t->next; + return t; +} + +// find thread by name +static threadinfo *findthread(char *name){ + if(!name) return NULL; + if(!thelist) return NULL; // thread list is empty + size_t l = strlen(name); + if(l < 1 || l > THREADNAMEMAXLEN) return NULL; + DBG("Try to find thread '%s'", name); + threadlist *lptr = thelist; + while(lptr){ + DBG("Check '%s'", lptr->ti.name); + if(strcmp(lptr->ti.name, name) == 0) return &lptr->ti; + lptr = lptr->next; + } + return NULL; +} + +// add message to queue +static char *addmesg(msgidx idx, message *msg, char *txt){ + if(idx < idxMOSI || idx > idxMISO){ + WARNX("Wrong message index"); + return NULL; + } + int addn = 0; + if(!msg) return NULL; + size_t L = strlen(txt); + if(L < 1) return NULL; + DBG("Want to add mesg '%s' with length %zd", txt, L); + if(pthread_mutex_lock(&msg->mutex[idx])) return NULL; + if(!pushmessage(&msg->text[idx], txt)) return NULL; + pthread_mutex_unlock(&msg->mutex[idx]); + return msg->text[idx]->data; +} + +// get all messages from queue (allocates data, should be free'd after usage!) +static char *getmesg(msgidx idx, message *msg){ + if(idx < idxMOSI || idx > idxMISO){ + WARNX("Wrong message index"); + return NULL; + } + if(!msg) return NULL; + char *text = NULL; + if(pthread_mutex_lock(&msg->mutex[idx])) return NULL; + text = popmessage(&msg->text[idx]); +ret: + pthread_mutex_unlock(&msg->mutex[idx]); + return text; +} + +/** + * @brief registerThread - register new thread + * @param name - thread name + * @param handler - thread handler + * @return pointer to new threadinfo struct or NULL if failed + */ +threadinfo *registerThread(char *name, void *(*handler)(void *)){ + threadinfo *ti = findthread(name); + if(!name || strlen(name) < 1 || !handler) return NULL; + DBG("Register new thread with name '%s'", name); + if(ti){ + WARNX("Thread named '%s' exists!", name); + return NULL; + } + if(!thelist){ // the first element + thelist = MALLOC(threadlist, 1); + ti = &thelist->ti; + }else{ + threadlist *last = getlast(); + last->next = MALLOC(threadlist, 1); + ti = &last->next->ti; + } + ti->handler = handler; + snprintf(ti->name, 31, "%s", name); + memset(&ti->mesg, 0, sizeof(ti->mesg)); + for(int i = 0; i < 2; ++i) + pthread_mutex_init(&ti->mesg.mutex[i], NULL); + if(pthread_create(&ti->thread, NULL, handler, (void*)ti)){ + WARN("pthread_create()"); + return NULL; + } + return ti; +} + +// kill and unregister thread with given name; @return - if all OK +int killThread(const char *name){ + if(!name || !thelist) return 1; + threadlist *lptr = thelist, *prev = NULL; + for(; lptr; lptr = lptr->next){ + if(strcmp(lptr->ti.name, name)){ + prev = lptr; + continue; + } + DBG("Found '%s', prev: '%s', delete", name, prev->ti.name); + threadlist *next = lptr->next; + if(lptr == thelist) thelist = next; + else if(prev) prev->next = next; + for(int i = 0; i < 2; ++i){ + pthread_mutex_lock(&lptr->ti.mesg.mutex[i]); + char *txt; + while((txt = popmessage(&lptr->ti.mesg.text[i]))) FREE(txt); + pthread_mutex_destroy(&lptr->ti.mesg.mutex[i]); + } + if(pthread_cancel(lptr->ti.thread)) WARN("Can't kill thread '%s'", name); + FREE(lptr); + return 0; + } + return 2; // not found +} + +static void *handler(void *data){ + threadinfo *ti = (threadinfo*)data; + while(1){ + char *got = getmesg(idxMOSI, &ti->mesg); + if(got){ + green("%s got: %s\n", ti->name, got); + FREE(got); + addmesg(idxMISO, &ti->mesg, "received"); + addmesg(idxMISO, &ti->mesg, "need more"); + } + usleep(100); + } + return NULL; +} + +static void dividemessages(message *msg, char *longtext){ + char *copy = strdup(longtext), *saveptr = NULL; + for(char *s = copy; ; s = NULL){ + char *nxt = strtok_r(s, " ", &saveptr); + if(!nxt) break; + addmesg(idxMOSI, msg, nxt); + } + FREE(copy); +} + +static void procmesg(char *text){ + if(!text) return; + char *nxt = strchr(text, ' '); + if(!nxt){ + WARNX("Usage: cmd data, where cmd:\n" + "\tnew threadname - create thread\n" + "\tdel threadname - delete thread\n" + "\tsend threadname data - send data to thread\n" + "\tsend all data - send data to all\n"); + return; + } + *nxt++ = 0; + if(strcasecmp(text, "new") == 0){ + registerThread(nxt, handler); + }else if(strcasecmp(text, "del") == 0){ + if(killThread(nxt)) WARNX("Can't delete '%s'", nxt); + }else if(strcasecmp(text, "send") == 0){ + text = strchr(nxt, ' '); + if(!text){ + WARNX("send all/threadname data"); + return; + } + *text++ = 0; + if(strcasecmp(nxt, "all") == 0){ // bcast + threadlist *lptr = thelist; + while(lptr){ + threadinfo *ti = &lptr->ti; + lptr = lptr->next; + green("Bcast send '%s' to thread '%s'\n", text, ti->name); + dividemessages(&ti->mesg, text); + } + }else{ // single + threadinfo *ti = findthread(nxt); + if(!ti){ + WARNX("Thread '%s' not found", nxt); + return; + } + green("Send '%s' to thread '%s'\n", text, nxt); + dividemessages(&ti->mesg, text); + } + } +} + +int main(){ + using_history(); + while(1){ + threadlist *lptr = thelist; + while(lptr){ + threadinfo *ti = &lptr->ti; + lptr = lptr->next; + char *got; + while((got = getmesg(idxMISO, &ti->mesg))){ + red("got from '%s': %s\n", ti->name, got); + fflush(stdout); + FREE(got); + } + } + char *text = readline("mesg > "); + if(!text) break; // ^D + if(strlen(text) < 1) continue; + add_history(text); + procmesg(text); + FREE(text); + } + return 0; +} +