-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
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
# 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)
#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
+#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;
}
--- /dev/null
+#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));
+}