Pthread pause based on pthread user data
authorTomas Mudrunka <tomas@mudrunka.cz>
Mon, 28 Jun 2021 15:44:20 +0000 (17:44 +0200)
committerTomas Mudrunka <tomas@mudrunka.cz>
Mon, 28 Jun 2021 15:44:20 +0000 (17:44 +0200)
c/pthread_extra/Makefile
c/pthread_extra/README.md
c/pthread_extra/pthread_extra.h
c/pthread_extra/pthread_pause.c
c/pthread_extra/pthread_user_data.c [new file with mode: 0644]

index cf4e67474bf7817aa8da742dffa357bc6fe6d522..0b96b41a16e61e1a4b29348345f8afd847f4a6cd 100644 (file)
@@ -1,4 +1,4 @@
-COPT=-O2 -flto -ftree-vectorize -ftree-slp-vectorize -fvisibility=hidden -D_FORTIFY_SOURCE=2 -DMG_ENABLE_FS=0 -fstack-protector-strong -fno-delete-null-pointer-checks $(CARCH)\r
+COPT=-g -O2 -flto -ftree-vectorize -ftree-slp-vectorize -fvisibility=hidden -D_FORTIFY_SOURCE=2 -DMG_ENABLE_FS=0 -fstack-protector-strong -fno-delete-null-pointer-checks $(CARCH)\r
 #COPT=-fvisibility=hidden -DMG_ENABLE_FS=0 -fstack-protector-strong $(CARCH)\r
 CDEF=-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE=1 -D_GNU_SOURCE\r
 CERR=-Wall -Wextra -Werror -pedantic -Werror=date-time -Wformat-security -Wduplicated-cond -Wfloat-equal -Wshadow -Wlogical-not-parentheses -Wnull-dereference\r
@@ -11,14 +11,14 @@ CXXFLAGS+=$(COPT) $(CERR) $(CDEF) -std=c++11 $(CLIB)
 LDFLAGS+=$(COPT) $(LIB)\r
 \r
 BIN=test\r
-OBJ=pthread_pause.o pthread_multi.o pthread_msgqueue.o test.o\r
+OBJ=pthread_user_data.o pthread_pause.o pthread_multi.o pthread_msgqueue.o test.o\r
 \r
 all: $(BIN)\r
 $(BIN): $(OBJ)\r
        $(CXX) -o $(BIN) $(OBJ) $(LDFLAGS)\r
        strip --strip-unneeded $(BIN)\r
 \r
-       gcc -lpthread -I . test_pause.c pthread_pause.o -o test_pause\r
+       gcc -lpthread -I . test_pause.c pthread_user_data.o pthread_pause.o -o test_pause\r
 \r
 clean:\r
        rm -f $(BIN) $(OBJ) $(DEPS)\r
index bf17f7e49987ca22958391f53681c550aaa62ebd..0824f473a1fd381749202eaa8c58c6bf10e09e9c 100644 (file)
@@ -1,10 +1,11 @@
 # pthread_extra
 
 Library implementing extra features on top of POSIX threads.
-Currently only tested on Linux. Mostly experimental code.
+Currently will probably only work on Linux. Mostly experimental code.
 
 ### Components
 
   * pthread_msgqueue - implements message queues, more features than POSIX mqueue (no known issues, but not really tested)
+  * pthread_user_data - allows user to store and retreive custom data using thread handle as a key (suboptimal, used internaly, not tested)
   * pthread_multi - lock multiple mutexes at once (might cause deadlocks in complex scenarios)
   * pthread_pause - implements suspend/resume functionality for pthreads (causes deadlocks under high load)
index bd1baad3b8e1270495b616ccd1b458fe568fd761..0652e0a568e146bc71844db1477f1c11f57b5a97 100644 (file)
@@ -2,26 +2,45 @@
 #define __PTHREAD_EXTRA_H__
 
 #include <pthread.h>
-#include <time.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <signal.h>
+//#include <time.h>
 
 #define PTHREAD_XTIME_NOBLOCK (&(struct timespec){ .tv_sec = 0, .tv_nsec = 0 })
 #define PTHREAD_XTIME_FOREVER NULL
 
-//Pausing
+// User data
 
+#define PTHREAD_XTHREADS_MAX 65535
+#define PTHREAD_XNULL ((pthread_t)NULL)
+
+#ifdef __PTHREAD_EXTRA_INTERNAL
+typedef struct pthread_user_data_internal_t {
+       pthread_t tid; //Thread ID
+       sig_atomic_t running; //Internaly used by pthread_pause
+       void *usr; //User pointer
+} pthread_user_data_internal_t;
+
+pthread_user_data_internal_t* pthread_user_data_internal(pthread_t thread);
+#endif //__PTHREAD_EXTRA_INTERNAL
+
+void** pthread_user_data_ptr(pthread_t thread);
+void*  pthread_user_data_get(pthread_t thread);
+void   pthread_user_data_set(pthread_t thread, void *usr);
+
+// Pausing
+
+//GDB: handle SIG34 nostop noprint
 #define PTHREAD_XSIG_STOP (SIGRTMIN+0)
 #define PTHREAD_XSIG_CONT (SIGRTMIN+1)
 #define PTHREAD_XSIGRTMIN (SIGRTMIN+2) //First unused RT signal
 
-#define pthread_pause(t)   (pthread_kill((t), PTHREAD_XSIG_STOP));
-#define pthread_unpause(t) (pthread_kill((t), PTHREAD_XSIG_CONT));
-
 void pthread_unpause_handler();
 void pthread_pause_handler();
 void pthread_pause_enable();
+int pthread_pause(pthread_t thread);
+int pthread_unpause(pthread_t thread);
 
 // Message queues
 
index 67a502331632de2ebf2acea1d6b322e06ce4824d..662d78fdd99a949e041a15c093f862c01d1f5a5d 100644 (file)
@@ -1,20 +1,64 @@
+#define __PTHREAD_EXTRA_INTERNAL
+
 #include <pthread.h>
 #include <pthread_extra.h>
 #include <signal.h>
-
-void pthread_unpause_handler() {
-       //NOP
-}
+#include <errno.h>
+#include <unistd.h>
+#include <sys/resource.h>
+//#include <stdio.h>
+//#include <sys/time.h>
 
 void pthread_pause_handler() {
+       //Do nothing when there are more signals pending (to cleanup the queue)
+       sigset_t pending;
+       sigpending(&pending);
+       if(sigismember(&pending, PTHREAD_XSIG_STOP)) return;
+
+       //Keep waiting for signals until we are supposed to be running
        sigset_t sigset;
        sigfillset(&sigset);
-       sigdelset(&sigset, PTHREAD_XSIG_CONT);
-       sigsuspend(&sigset);
-       //int sig; sigwait(&sigset, &sig);
+       sigdelset(&sigset, PTHREAD_XSIG_STOP);
+       while(!pthread_user_data_internal(pthread_self())->running) {
+               sigsuspend(&sigset);
+       }
 }
 
 void pthread_pause_enable() {
+       //Nesting signals too deep is not good for stack
+       //You can get runtime stats using following command:
+       //grep -i sig /proc/$(pgrep binary)/status
+       struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32};
+       setrlimit(RLIMIT_SIGPENDING, &sigq);
+
+       //Setup signal handler
        signal(PTHREAD_XSIG_STOP, pthread_pause_handler);
-       signal(PTHREAD_XSIG_CONT, pthread_unpause_handler);
+
+       //Unblock signal
+       sigset_t sigset;
+       sigemptyset(&sigset);
+       sigaddset(&sigset, PTHREAD_XSIG_STOP);
+       pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+}
+
+void pthread_pause_disable() {
+       //Block signal
+       sigset_t sigset;
+       sigemptyset(&sigset);
+       sigaddset(&sigset, PTHREAD_XSIG_STOP);
+       pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+}
+
+int pthread_pause(pthread_t thread) {
+       //Set thread as paused and notify it via signal (wait when queue full)
+       pthread_user_data_internal(thread)->running = 0;
+       while(pthread_kill(thread, PTHREAD_XSIG_STOP) == EAGAIN) usleep(1000);
+       return 0;
+}
+
+int pthread_unpause(pthread_t thread) {
+       //Set thread as running and notify it via signal (wait when queue full)
+       pthread_user_data_internal(thread)->running = 1;
+       while(pthread_kill(thread, PTHREAD_XSIG_STOP) == EAGAIN) usleep(1000);
+       return 0;
 }
diff --git a/c/pthread_extra/pthread_user_data.c b/c/pthread_extra/pthread_user_data.c
new file mode 100644 (file)
index 0000000..b86c4a3
--- /dev/null
@@ -0,0 +1,43 @@
+#define __PTHREAD_EXTRA_INTERNAL
+
+#include <pthread.h>
+#include <pthread_extra.h>
+
+//Static array with user data for all thread handles
+//TODO: perhaps use something more sophisticated like linked list?
+pthread_user_data_internal_t pthread_user_data[PTHREAD_XTHREADS_MAX+1] = {{.tid=PTHREAD_XNULL}};
+
+//Get pointer to internal record tied to specified thread
+pthread_user_data_internal_t* pthread_user_data_internal(pthread_t thread) {
+       //Return NULL if requested thread handle is NULL
+       if(pthread_equal(thread, PTHREAD_XNULL)) return NULL;
+
+       //Find if the thread is already registered, add it if not
+       //FIXME: recycle slots of destroyed threads!!!
+       pthread_t i;
+       for(i = 0; i<PTHREAD_XTHREADS_MAX; i++) {
+               if(pthread_equal(pthread_user_data[i].tid, PTHREAD_XNULL)) {
+                       pthread_user_data[i].tid = thread;
+                       pthread_user_data[i+1].tid = PTHREAD_XNULL;
+                       break;
+               }
+               if(pthread_equal(pthread_user_data[i].tid, thread)) break;
+       }
+       //Return pointer
+       return &pthread_user_data[i];
+}
+
+//Get pointer to user specified pointer of that thread
+void** pthread_user_data_ptr(pthread_t thread) {
+       return &pthread_user_data_internal(thread)->usr;
+}
+
+//Set user specified pointer for thread
+void pthread_user_data_set(pthread_t thread, void *usr) {
+       *(pthread_user_data_ptr(thread)) = usr;
+}
+
+//Get user specified pointer for thread
+void* pthread_user_data_get(pthread_t thread) {
+       return *(pthread_user_data_ptr(thread));
+}
This page took 0.258182 seconds and 4 git commands to generate.