File: lwp_ucontext.c

package info (click to toggle)
lwp 2.4%2Bdebian-4
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 1,800 kB
  • ctags: 615
  • sloc: sh: 9,105; ansic: 2,927; asm: 687; makefile: 129
file content (171 lines) | stat: -rw-r--r-- 4,447 bytes parent folder | download | duplicates (2)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* BLURB gpl

                           Coda File System
                              Release 5

          Copyright (c) 2005 Carnegie Mellon University
                  Additional copyrights listed below

This  code  is  distributed "AS IS" without warranty of any kind under
the terms of the GNU General Public Licence Version 2, as shown in the
file  LICENSE.  The  technical and financial  contributors to Coda are
listed in the file CREDITS.

                        Additional copyrights

#*/

#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include "lwp_ucontext.h"

/* if we already have ucontext.h we don't need any of this */
#ifndef CODA_USE_UCONTEXT

/* Some architectures have a stack that grows up instead of down */
//#define STACK_GROWS_UP 1

/* next most portable alternative is to use sigaltstack */
#ifdef HAVE_SIGALTSTACK
jmp_buf parent;
#define returnto(ctx) longjmp(*ctx, 1)
#else
/* otherwise, use the old LWP savecontext/returnto assembly routines */
struct lwp_context {	/* saved context for dispatcher */
    char *topstack;	/* ptr to top of process stack */
    char *returnadd;	/* return address ? */
    char *ccr;		/* Condition code register */
};

int savecontext (void (*f)(void), struct lwp_context *ctx, char *stack);
int returnto (struct lwp_context *ctx);

static struct lwp_context parent;
#endif

/* make stacks 16-byte aligned and leave space for silly LinuxPPC lunkage, or
 * we segfault entering functions --troy */
#define STACK_PAD 64

/* global variables used when initializing new threads */
static ucontext_t *child;
static void (*child_func)();
static int child_argc;
static void *child_arg;

/* helper function to start a new thread */
static void _thread(void)
{
    ucontext_t *this = child;
    void (*func)() = child_func;
    int argc = child_argc;
    void *arg = child_arg;
    child = NULL;

    if (sigsetjmp(this->uc_mcontext, 0) == 0)
	returnto(&parent);
    
    /* restore signal mask */
    sigprocmask(SIG_SETMASK, &this->uc_sigmask, NULL);

    if (argc)
	func(arg);
    else
	func();

    if (this->uc_link)
	setcontext(this->uc_link);

    /* according to the makecontext documentation, this 'thread' now exits.
     * But we don't know which other 'context' we should jump to... */
    exit(0);
}

/* Implementations for getcontext, setcontext, makecontext, and swapcontext */
void _lwp_initctx(ucontext_t *ucp)
{
    sigset_t sigempty;
    sigemptyset(&sigempty);
    memset(ucp, 0, sizeof(ucontext_t));

    /* save the signal mask of the current thread */
    sigprocmask(SIG_BLOCK, &sigempty, &ucp->uc_sigmask);
}

int setcontext(const ucontext_t *ucp)
{
    siglongjmp(*(sigjmp_buf *)&ucp->uc_mcontext, 1);
    assert(0); /* we should never get here */
}

int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
{
    if (sigsetjmp(oucp->uc_mcontext, 1) == 0)
	siglongjmp(ucp->uc_mcontext, 1);
    return 0;
}

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
{
    va_list ap;
    char *stack = ucp->uc_stack.ss_sp;

#ifdef HAVE_SIGALTSTACK
    struct sigaction action, oldaction;
    sigset_t sigs, oldsigs;
    stack_t oldstack;
#endif

    assert(stack != NULL);

    child = ucp;
    child_func = func;
    child_argc = argc;

    if (argc) {
	assert(argc <= 1);
	va_start(ap, argc);
	child_arg = va_arg(ap, void *);
	va_end(ap);
    }

#ifndef HAVE_SIGALTSTACK
#ifndef STACK_GROWS_UP
    stack += (ucp->uc_stack.ss_size-1) & ~(STACK_PAD-1);
#endif

    /* The old lwp technique looks a lot simpler now doesn't it. However
     * it requires processor specific assembly which is a PITA */
    savecontext(_thread, &parent, stack);

#else /* HAVE_SIGALTSTACK */

    action.sa_handler = (void(*)(int))_thread;
    action.sa_flags = SA_ONSTACK;
    sigemptyset(&action.sa_mask);

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGUSR1);

    /* we use a signal to jump into the new stack */
    sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
    sigaltstack(&ucp->uc_stack, &oldstack);
    sigaction(SIGUSR1, &action, &oldaction);

    kill(getpid(), SIGUSR1);

    /* handle the SIGUSR1 signal */
    sigfillset(&sigs);
    sigdelset(&sigs, SIGUSR1);
    if (!setjmp(parent))
	while (child)
	    sigsuspend(&sigs);

    /* The new context should be set up, now revert to the old state... */
    sigaltstack(&oldstack, NULL);
    sigaction(SIGUSR1, &oldaction, NULL);
    sigprocmask(SIG_SETMASK, &oldsigs, NULL);
#endif /* HAVE_SIGALTSTACK */
}
#endif