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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
/*
** GNU Pth - The GNU Portable Threads
** Copyright (c) 1999-2004 Ralf S. Engelschall <rse@engelschall.com>
**
** This file is part of GNU Pth, a non-preemptive thread scheduling
** library which can be found at http://www.gnu.org/software/pth/.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
**
** pth_uctx.c: Pth user-space context handling (stand-alone sub-API)
*/
/* ``It worries me however, to realize
how tough an ass-hole I have had to
be, in order to get to stick to the
principle of doing things right,
rather than "just hack it in".''
-- Poul-Henning Kamp <phk@FreeBSD.org> */
#include "pth_p.h"
/* user-space context structure */
struct pth_uctx_st {
int uc_stack_own; /* whether stack were allocated by us */
char *uc_stack_ptr; /* pointer to start address of stack area */
size_t uc_stack_len; /* size of stack area */
int uc_mctx_set; /* whether uc_mctx is set */
pth_mctx_t uc_mctx; /* saved underlying machine context */
};
/* create user-space context structure */
int
pth_uctx_create(
pth_uctx_t *puctx)
{
pth_uctx_t uctx;
/* argument sanity checking */
if (puctx == NULL)
return pth_error(FALSE, EINVAL);
/* allocate the context structure */
if ((uctx = (pth_uctx_t)malloc(sizeof(struct pth_uctx_st))) == NULL)
return pth_error(FALSE, errno);
/* initialize the context structure */
uctx->uc_stack_own = FALSE;
uctx->uc_stack_ptr = NULL;
uctx->uc_stack_len = 0;
uctx->uc_mctx_set = FALSE;
memset((void *)&uctx->uc_mctx, 0, sizeof(pth_mctx_t));
/* pass result to caller */
*puctx = uctx;
return TRUE;
}
/* trampoline context */
typedef struct {
pth_mctx_t *mctx_parent;
pth_uctx_t uctx_this;
pth_uctx_t uctx_after;
void (*start_func)(void *);
void *start_arg;
} pth_uctx_trampoline_t;
pth_uctx_trampoline_t pth_uctx_trampoline_ctx;
/* trampoline function for pth_uctx_make() */
static void pth_uctx_trampoline(void)
{
volatile pth_uctx_trampoline_t ctx;
/* move context information from global to local storage */
ctx.mctx_parent = pth_uctx_trampoline_ctx.mctx_parent;
ctx.uctx_this = pth_uctx_trampoline_ctx.uctx_this;
ctx.uctx_after = pth_uctx_trampoline_ctx.uctx_after;
ctx.start_func = pth_uctx_trampoline_ctx.start_func;
ctx.start_arg = pth_uctx_trampoline_ctx.start_arg;
/* switch back to parent */
pth_mctx_switch(&(ctx.uctx_this->uc_mctx), ctx.mctx_parent);
/* enter start function */
(*ctx.start_func)(ctx.start_arg);
/* switch to successor user-space context */
if (ctx.uctx_after != NULL)
pth_uctx_restore(ctx.uctx_after);
/* terminate process (the only reasonable thing to do here) */
exit(0);
/* NOTREACHED */
return;
}
/* make setup of user-space context structure */
int
pth_uctx_make(
pth_uctx_t uctx,
char *sk_addr, size_t sk_size,
const sigset_t *sigmask,
void (*start_func)(void *), void *start_arg,
pth_uctx_t uctx_after)
{
pth_mctx_t mctx_parent;
sigset_t ss;
/* argument sanity checking */
if (uctx == NULL || start_func == NULL || sk_size < 16*1024)
return pth_error(FALSE, EINVAL);
/* configure run-time stack */
if (sk_addr == NULL) {
if ((sk_addr = (char *)malloc(sk_size)) == NULL)
return pth_error(FALSE, errno);
uctx->uc_stack_own = TRUE;
}
else
uctx->uc_stack_own = FALSE;
uctx->uc_stack_ptr = sk_addr;
uctx->uc_stack_len = sk_size;
/* configure the underlying machine context */
if (!pth_mctx_set(&uctx->uc_mctx, pth_uctx_trampoline,
uctx->uc_stack_ptr, uctx->uc_stack_ptr+uctx->uc_stack_len))
return pth_error(FALSE, errno);
/* move context information into global storage for the trampoline jump */
pth_uctx_trampoline_ctx.mctx_parent = &mctx_parent;
pth_uctx_trampoline_ctx.uctx_this = uctx;
pth_uctx_trampoline_ctx.uctx_after = uctx_after;
pth_uctx_trampoline_ctx.start_func = start_func;
pth_uctx_trampoline_ctx.start_arg = start_arg;
/* optionally establish temporary signal mask */
if (sigmask != NULL)
sigprocmask(SIG_SETMASK, sigmask, &ss);
/* perform the trampoline step */
pth_mctx_switch(&mctx_parent, &uctx->uc_mctx);
/* optionally restore original signal mask */
if (sigmask != NULL)
sigprocmask(SIG_SETMASK, &ss, NULL);
/* finally flag that the context is now configured */
uctx->uc_mctx_set = TRUE;
return TRUE;
}
/* save current user-space context */
int
pth_uctx_save(
pth_uctx_t uctx)
{
/* argument sanity checking */
if (uctx == NULL)
return pth_error(FALSE, EINVAL);
/* save underlying machine context */
pth_mctx_save(&uctx->uc_mctx);
uctx->uc_mctx_set = TRUE;
return TRUE;
}
/* restore current user-space context */
int
pth_uctx_restore(
pth_uctx_t uctx)
{
/* argument sanity checking */
if (uctx == NULL)
return pth_error(FALSE, EINVAL);
if (!(uctx->uc_mctx_set))
return pth_error(FALSE, EPERM);
/* restore underlying machine context */
pth_mctx_restore(&uctx->uc_mctx);
return TRUE;
}
/* switch from current to other user-space context */
int
pth_uctx_switch(
pth_uctx_t uctx_from,
pth_uctx_t uctx_to)
{
/* argument sanity checking */
if (uctx_from == NULL || uctx_to == NULL)
return pth_error(FALSE, EINVAL);
if (!(uctx_to->uc_mctx_set))
return pth_error(FALSE, EPERM);
/* switch underlying machine context */
uctx_from->uc_mctx_set = TRUE;
pth_mctx_switch(&uctx_from->uc_mctx, &uctx_to->uc_mctx);
return TRUE;
}
/* destroy user-space context structure */
int
pth_uctx_destroy(
pth_uctx_t uctx)
{
/* argument sanity checking */
if (uctx == NULL)
return pth_error(FALSE, EINVAL);
/* deallocate dynamically allocated stack */
if (uctx->uc_stack_own && uctx->uc_stack_ptr != NULL)
free(uctx->uc_stack_ptr);
/* deallocate context structure */
free(uctx);
return TRUE;
}
|