Using ucontext_t struct in signal

Hello everyone,
I want to switch between user contexts using a signal handler (something like a
preemptive scheduler for userlevel threads). I've found several sources, which say that it's not a good idea to use setcontext or swapcontext in a signal handler.
Nevertheless there also exists at least one sample code of such an preemptive scheduler, which seems to work well, at least on my machine (Ubuntu 8.04 with linux kernel 2.6.24-22): www.seas.upenn.edu/~cse381/context_demo.c

// [...]
static ucontext_t thread_context;
static ucontext_t scheduler_context;

int thread_finished;
int i;

static void simple_function(void) {
// do nothing but counting
for (i = 0; i < 1000000000; ++i) { }

if (i == 1000000000) {
printf("\n[Thread Function]\t1st counting worked fine...");
} else {
printf("\n[Thread Function]\tError: Counting didn't finished (%d)...", i);
}

thread_finished = 1;
}

static void other_function() {
// thread_finished is a global variable, which is set to 1, if the thread function \
is finished while(thread_finished != 1) { swapcontext(&scheduler_context, &thread_context); } }

static void signal_handler_function(int sig_nr, siginfo_t* info, void *old_context) {
if (sig_nr == SIGPROF) {
// saves the thread context
thread_context = *((ucontext_t*) old_context);

// swaps back to scheduler context
setcontext(&scheduler_context);
}
}
// [...]

I ran into the following problem which belongs to the code above. I interrupted simple_function several times by using a ITimer. But the for-loop doesn't finish successfully everytime. Often the if condition is false. But it does not cancel after the first signal is raised. I've found out that using the third parameter old_context for storing the old context is the reason. But I don't know why. So I thought there might be a problem in the kernel. Am I right? I was afraid to post the whole code, so I hope that this code snippet is enough. I would appreciate if someone can give me a comment whether this strange behaviour is because of a wrong thinking of mine or because of a error in the kernel which needs to be fixed.

Thanks a lot!
Matthias
Last edited on
Where is scheduler_context initialised?
Both, scheduler_context and thread_context are initialized in the main function. Here is the complete code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// signal handling
#include <signal.h>
// context switch
#include <ucontext.h>
// timer
#include <sys/time.h>

// upper bound of the for loops inside thread_function
#define UPPER_BOUND 100000000
// number of seconds for setting the interval used by the timer
#define QUANTUM_SEC 0
// number of microseconds for setting the interval used by the timer (0 - 999999)
#define QUANTUM_MICRO_SEC 100000

#define STACKSIZE 4096

static ucontext_t thread_context;
static ucontext_t scheduler_context;

int thread_finished;
int i;

static void thread_function(void) {
	printf("\n[Thread Function]\tFunction starts...");

	for (i = 0; i < UPPER_BOUND; ++i) {
		// do nothing but counting
	}

	if (i == UPPER_BOUND) {
		printf("\n[Thread Function]\t1st counting worked fine...");
	} else {
		printf("\n[Thread Function]\tError: 1st counting didn't finished (%d)...", i);
	}

	for (i = 0; i < UPPER_BOUND; ++i) {
		// do nothing but counting
	}

	if (i == UPPER_BOUND) {
		printf("\n[Thread Function]\t2nd counting worked fine...");
	} else {
		printf("\n[Thread Function]\tError: 2nd counting didn't finished (%d)...", i);
	}

	thread_finished = 1;

	printf("\n[Thread Function]\tFunction finishes...");
}

static void scheduler_function() {

	printf("\n[Scheduler Function]\tScheduler starts...");

	// thread_finished is a global variable, which is set to 1, if the thread function is finished
	while(thread_finished != 1) {
		swapcontext(&scheduler_context, &thread_context);
		//printf("\n[Scheduler Function]\tSwap back is done...");
	}

	printf("\n[Scheduler Function]\tScheduler finishes...");
}

static void signal_handler_function(int sig_nr, siginfo_t* info, void *old_context) {

	if (sig_nr == SIGPROF) {
		printf("\n[Signal Handler]\tSIGPROF was raised at %d...", i);

		// saves the thread context
		thread_context = *((ucontext_t*) old_context);

		// swaps back to scheduler context
		setcontext(&scheduler_context);
	} else {
		printf("\n[Signal Handler]\tA different signal was raised...");
		return;
	}

}

int main(int argc, char **argv) {

	printf("\n[Main Function]\t\tProgram starts...");

	thread_finished = 0;

	char thread_stack[STACKSIZE];
	char scheduler_stack[STACKSIZE];

	// initializing scheduler context
	if (getcontext(&scheduler_context) == 0) {
		scheduler_context.uc_stack.ss_sp = scheduler_stack;
		scheduler_context.uc_stack.ss_size = sizeof(scheduler_stack);
		scheduler_context.uc_stack.ss_flags = 0;
		scheduler_context.uc_link = NULL;
		printf("\n[Main Function]\t\tscheduler_context was initialized...");
	} else {
		printf("\n[Main Function]\t\tError while initializing scheduler_context...");
		exit(-1);
	}

	// initializing thread context
	if (getcontext(&thread_context) == 0) {
		thread_context.uc_stack.ss_sp = thread_stack;
		thread_context.uc_stack.ss_size = sizeof(thread_stack);
		thread_context.uc_stack.ss_flags = 0;
		thread_context.uc_link = &scheduler_context;
		printf("\n[Main Function]\t\tthread_context was initialized...");
	} else {
		printf("\n[Main Function]\t\tError while initializing thread_context...");
		exit(-1);
	}

	// sets the signal handler for swapping to the scheduler context
	struct sigaction scheduling_interuption_handler;
	scheduling_interuption_handler.sa_sigaction = signal_handler_function;
	scheduling_interuption_handler.sa_flags = SA_RESTART | SA_SIGINFO;
	sigemptyset(&scheduling_interuption_handler.sa_mask);
	if (sigaction(SIGPROF, &scheduling_interuption_handler, NULL) == -1) {
		printf("\n[Main Function]\t\tAn error occurred while initializing the signal handler for swapping to the scheduler context...");
		exit(-1);
	}

	// sets the timer which sends SIGPROF periodically
	struct itimerval timeslice;
	timeslice.it_value.tv_sec = QUANTUM_SEC;
	timeslice.it_value.tv_usec = QUANTUM_MICRO_SEC;
	timeslice.it_interval = timeslice.it_value;

	if (setitimer(ITIMER_PROF, &timeslice, NULL) == 0) {
		printf("\n[Main Function]\t\tThe timer was initialized...");
	} else {
		printf("\n[Main Function]\t\tAn error occurred while executing setitimer...");
		exit(-1);
	}

	// sets the thread function
	makecontext(&thread_context, thread_function, 0);

	// this function handles the swapping part
	scheduler_function();

	printf("\n[Main Function]\t\tProgram finishes...");
}


And here is an example for an output:
[Main Function] Program starts...
[Main Function] scheduler_context was initialized...
[Main Function] thread_context was initialized...
[Main Function] The timer was initialized...
[Scheduler Function] Scheduler starts...
[Thread Function] Function starts...
[Signal Handler] SIGPROF was raised at 30811962...
[Signal Handler] SIGPROF was raised at 62309593...
[Signal Handler] SIGPROF was raised at 93262549...
[Thread Function] 1st counting worked fine...
[Signal Handler] SIGPROF was raised at 24910729...
[Signal Handler] SIGPROF was raised at 55303135...
[Signal Handler] SIGPROF was raised at 86507356...
[Thread Function] Error: 2nd counting didn't finished (86507356)...
[Thread Function] Function finishes...
[Scheduler Function] Scheduler finishes...
[Main Function] Program finishes...


I hope you can work with this.
Last edited on
Topic archived. No new replies allowed.