| /* JSGF: no idea what this is actually doing, but it really gives the |
| signals/sigaltstack/threads machinery a working out */ |
| /** |
| * Compile with: |
| * gcc -g -Wall -lpthread -o susphello susphello.c |
| * |
| * Author Magnus Ihse, ihse at bea.com |
| */ |
| |
| #include <signal.h> |
| |
| |
| #include <errno.h> |
| #include <stddef.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/resource.h> |
| #include <unistd.h> |
| #include <dlfcn.h> |
| |
| |
| #include <pthread.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <netdb.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #define THREAD_COUNT 10 |
| #define ITER_COUNT 200 |
| |
| static volatile int finishedArray[THREAD_COUNT]; |
| static int pKey; |
| |
| static sigset_t srSigset; |
| |
| pthread_t main_thread; |
| |
| int srSignal = SIGUSR1; |
| |
| |
| void |
| ptiSrSigHandler(int sig, siginfo_t *sip, void *arg) |
| { |
| //ucontext_t *ucontext = (ucontext_t *)arg; |
| |
| int mypos = (int) pthread_getspecific(pKey); |
| |
| |
| // int threadPos = (int)pthread_getspecific(srThreadKey); |
| |
| // thread->os_context = (OSContextP)&(ucontext->uc_mcontext); |
| |
| // Notify suspender that we have been suspended |
| if (pthread_kill(main_thread, srSignal) == -1) { |
| perror("pthread_kill"); |
| exit(1); |
| } |
| |
| finishedArray[mypos]++; |
| |
| // printf("this is thread %d: i'm now suspended!\n", mypos); |
| |
| // Wait until we are resumed |
| while (sigwaitinfo(&srSigset, NULL) == -1) { |
| // Interrupted by SIGSTOP in gdb |
| if(errno != EINTR) { |
| perror("sigwaitinfo"); |
| exit(1); |
| } |
| } |
| |
| // printf("this is thread %d: i'm now resumed!\n", mypos); |
| |
| //thread->os_context = NULL; // just for the sake of it... |
| |
| // Notify resumer that we have been resumed |
| if (pthread_kill(main_thread, srSignal) == -1) { |
| perror("pthread_kill"); |
| exit(1); |
| } |
| // printf("this is thread %d: and I've told Master!!\n", mypos); |
| |
| } |
| |
| |
| void |
| suspendOrResume(pthread_t thread, int i) |
| { |
| sigset_t oss; |
| |
| // Mask out suspend/resume signal until we explicitly wait for it |
| sigprocmask(SIG_BLOCK, &srSigset, &oss); |
| |
| // Send signal to suspend or resume the thread |
| if (pthread_kill(thread, srSignal) == -1) { |
| perror("pthread_kill"); |
| exit(1); |
| } |
| |
| // printf("sent signal to %d...", i); |
| // Wait for notification from thread being suspended/resumed |
| while (sigwaitinfo(&srSigset, NULL) == -1) { |
| // Interrupted by SIGSTOP in gdb |
| if(errno != EINTR) { |
| perror("sigwaitinfo"); |
| exit(1); |
| } |
| } |
| |
| // Restore original signal mask |
| sigprocmask(SIG_SETMASK, &oss, NULL); |
| |
| // printf("... okay, %d suspended\n", i); |
| } |
| |
| |
| |
| void |
| initSignalling(void) |
| { |
| struct sigaction sa; |
| |
| // Signal mask for suspend/resume |
| sigemptyset(&srSigset); |
| sigaddset(&srSigset, srSignal); |
| |
| // Set up signal handler for suspend/resume |
| sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; |
| sa.sa_sigaction = ptiSrSigHandler; |
| sigfillset(&sa.sa_mask); |
| |
| # if !defined(_AIX) |
| /* jrs 20060615: is this important? I don't know. */ |
| sigdelset(&sa.sa_mask, (__SIGRTMIN+1)); |
| # endif |
| |
| if (sigaction(srSignal, &sa, 0) == -1) { |
| perror("sigaction"); |
| exit(1); |
| } |
| |
| // Unblock suspend signal |
| sigprocmask(SIG_UNBLOCK, &srSigset, 0); |
| |
| main_thread = pthread_self(); |
| } |
| |
| |
| void* setup_altstack(void) { |
| stack_t ss; |
| |
| ss.ss_sp = malloc(20*1024); |
| if (ss.ss_sp == 0) { |
| return NULL; |
| } |
| ss.ss_size = 20*1024; |
| ss.ss_flags = 0; |
| |
| if (sigaltstack(&ss, NULL) == -1) { |
| perror("sigaltstack"); |
| return NULL; |
| } |
| return ss.ss_sp; |
| } |
| |
| void takedown_altstack(void* stack) { |
| # if defined(_AIX) |
| stack_t ss; |
| # else |
| struct sigaltstack ss; |
| # endif |
| int result; |
| |
| ss.ss_flags = SS_DISABLE; |
| ss.ss_sp = (void*)47; // This value should be ignored when ss_flags is SS_DISABLE |
| ss.ss_size = 29; // This value should be ignored when ss_flags is SS_DISABLE |
| |
| { |
| result = sigaltstack(&ss, NULL); |
| free(stack); |
| } |
| } |
| |
| void *threadfunc(void *arg) { |
| int mypos = (int)arg; |
| int i; |
| long square = 1; |
| void* altstack = setup_altstack(); |
| |
| pthread_setspecific(pKey, arg); |
| for (i=0; i < 1000; i++) { |
| square = i*i + square*mypos; |
| } |
| |
| // wait for signal |
| while (finishedArray[mypos] == 0) { |
| struct timespec req, rem; |
| |
| req.tv_sec = 0; |
| req.tv_nsec = 5 * 1000 * 1000; |
| |
| nanosleep(&req, &rem); |
| |
| }; |
| |
| finishedArray[mypos]++; |
| |
| takedown_altstack(altstack); |
| |
| return NULL; |
| } |
| |
| |
| int main(int argc, char ** argv) { |
| pthread_t threads[THREAD_COUNT]; |
| pthread_attr_t attr; |
| int result; |
| int i; |
| int iteration; |
| int finished; |
| |
| initSignalling(); |
| |
| pthread_attr_init(&attr); |
| pthread_attr_setstacksize(&attr, 128*1024); |
| |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| |
| pthread_key_create(&pKey, NULL); |
| |
| for (iteration = 0; iteration < ITER_COUNT; iteration++) { |
| #if 0 |
| if ((iteration % 100) == 0) { |
| printf("\nStarting run series %i: ", iteration); |
| } |
| |
| if ((iteration % 10) == 0) { |
| printf("."); |
| fflush(stdout); |
| } |
| #endif |
| |
| // Clear array |
| for (i = 0; i< THREAD_COUNT; i++) { |
| finishedArray[i] = 0; |
| } |
| |
| // Start threads |
| for (i = 0; i< THREAD_COUNT; i++) { |
| result = pthread_create(&threads[i], &attr, threadfunc, (void*)i); |
| if (result != 0) { |
| perror("pthread_create"); |
| exit(1); |
| } |
| } |
| |
| // printf("all threads started\n"); |
| // suspend threads |
| for (i = 0; i< THREAD_COUNT; i++) { |
| suspendOrResume(threads[i], i); |
| } |
| |
| // printf("now all threads are suspended\n"); |
| |
| // resume threads |
| for (i = 0; i< THREAD_COUNT; i++) { |
| suspendOrResume(threads[i], i); |
| } |
| |
| |
| // Join threads |
| /* |
| printf("about to join..."); |
| for (i = 0; i< THREAD_COUNT; i++) { |
| result = pthread_join(threads[i], NULL); |
| if (result != 0) { |
| perror("pthread_join"); |
| exit(1); |
| } |
| } |
| |
| printf("...joined"); |
| */ |
| // printf("Spin waiting for results\n"); |
| finished = 1; |
| do { |
| struct timespec req, rem; |
| |
| req.tv_sec = 0; |
| req.tv_nsec = 5 * 1000 * 1000; |
| finished = 1; |
| |
| nanosleep(&req, &rem); |
| |
| // sleep(1); |
| for (i = 0; i< THREAD_COUNT; i++) { |
| if (finishedArray[i] < 2) { |
| finished = 0; |
| // printf("no result at: %d, value: %d\n", i, finishedArray[i]); |
| break; |
| } |
| } |
| // sleep(1); |
| } while (!finished); |
| |
| } |
| |
| printf("PASSED\n"); |
| return 0; |
| } |