]> git.ozlabs.org Git - ccan/blob - ccan/coroutine/coroutine.c
coroutine: Enable valgrind
[ccan] / ccan / coroutine / coroutine.c
1 /* GNU LGPL version 2 (or later) - see LICENSE file for details */
2 #include <assert.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <inttypes.h>
6 #include <stdlib.h>
7
8 #include <ccan/ptrint/ptrint.h>
9 #include <ccan/compiler/compiler.h>
10 #include <ccan/build_assert/build_assert.h>
11 #include <ccan/coroutine/coroutine.h>
12
13 /*
14  * Stack management
15  */
16
17 /* Returns lowest stack addres, regardless of growth direction */
18 static UNNEEDED void *coroutine_stack_base(struct coroutine_stack *stack)
19 {
20 #if HAVE_STACK_GROWS_UPWARDS
21         return (char *)(stack + 1);
22 #else
23         return (char *)stack - stack->size;
24 #endif
25 }
26
27 #if HAVE_VALGRIND_MEMCHECK_H
28 #include <valgrind/memcheck.h>
29 static void vg_register_stack(struct coroutine_stack *stack)
30 {
31         char *base = coroutine_stack_base(stack);
32
33         VALGRIND_MAKE_MEM_UNDEFINED(base, stack->size);
34         stack->valgrind_id = VALGRIND_STACK_REGISTER(base,
35                                                      base + stack->size - 1);
36 }
37
38 static void vg_deregister_stack(struct coroutine_stack *stack)
39 {
40         VALGRIND_MAKE_MEM_UNDEFINED(coroutine_stack_base(stack), stack->size);
41         VALGRIND_STACK_DEREGISTER(stack->valgrind_id);
42 }
43 static bool vg_addressable(void *p, size_t len)
44 {
45         return !VALGRIND_CHECK_MEM_IS_ADDRESSABLE(p, len);
46 }
47 #else
48 #define vg_register_stack(stack)                do { } while (0)
49 #define vg_deregister_stack(stack)              do { } while (0)
50 #define vg_addressable(p, len)                  (true)
51 #endif
52
53 struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
54                                              size_t metasize)
55 {
56         struct coroutine_stack *stack;
57         size_t size = bufsize - sizeof(*stack) - metasize;
58
59 #ifdef MINSIGSTKSZ
60         BUILD_ASSERT(COROUTINE_MIN_STKSZ >= MINSIGSTKSZ);
61 #endif
62
63         if (bufsize < (COROUTINE_MIN_STKSZ + sizeof(*stack) + metasize))
64                 return NULL;
65
66 #if HAVE_STACK_GROWS_UPWARDS
67         stack = (char *)buf + metasize;
68 #else
69         stack = (struct coroutine_stack *)
70                 ((char *)buf + bufsize - metasize) - 1;
71 #endif
72
73         stack->magic = COROUTINE_STACK_MAGIC;
74         stack->size = size;
75         vg_register_stack(stack);
76         return stack;
77 }
78
79 void coroutine_stack_release(struct coroutine_stack *stack, size_t metasize)
80 {
81         vg_deregister_stack(stack);
82         memset(stack, 0, sizeof(*stack));
83 }
84
85 struct coroutine_stack *coroutine_stack_check(struct coroutine_stack *stack,
86                                               const char *abortstr)
87 {
88         if (stack && vg_addressable(stack, sizeof(*stack))
89             && (stack->magic == COROUTINE_STACK_MAGIC)
90             && (stack->size >= COROUTINE_MIN_STKSZ))
91                 return stack;
92
93         if (abortstr) {
94                 if (!stack)
95                         fprintf(stderr, "%s: NULL coroutine stack\n", abortstr);
96                 else
97                         fprintf(stderr,
98                                 "%s: Bad coroutine stack at %p (magic=0x%"PRIx64" size=%zd)\n",
99                                 abortstr, stack, stack->magic, stack->size);
100                 abort();
101         }
102         return NULL;
103 }
104
105 size_t coroutine_stack_size(const struct coroutine_stack *stack)
106 {
107         return stack->size;
108 }
109
110 #if HAVE_UCONTEXT
111 static void coroutine_uc_stack(stack_t *uc_stack,
112                                const struct coroutine_stack *stack)
113 {
114         uc_stack->ss_size = coroutine_stack_size(stack);
115         uc_stack->ss_sp = coroutine_stack_base((struct coroutine_stack *)stack);
116 }
117 #endif /* HAVE_UCONTEXT */
118
119 /*
120  * Coroutine switching
121  */
122
123 #if HAVE_UCONTEXT
124 void coroutine_init_(struct coroutine_state *cs,
125                      void (*fn)(void *), void *arg,
126                      struct coroutine_stack *stack)
127 {
128         getcontext (&cs->uc);
129
130         coroutine_uc_stack(&cs->uc.uc_stack, stack);
131
132         if (HAVE_POINTER_SAFE_MAKECONTEXT) {
133                 makecontext(&cs->uc, (void *)fn, 1, arg);
134         } else {
135                 ptrdiff_t si = ptr2int(arg);
136                 ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
137                 int lo = si & mask;
138                 int hi = si >> (sizeof(int) * 8);
139
140                 makecontext(&cs->uc, (void *)fn, 2, lo, hi);
141         }
142         
143 }
144
145 void coroutine_jump(const struct coroutine_state *to)
146 {
147         setcontext(&to->uc);
148         assert(0);
149 }
150
151 void coroutine_switch(struct coroutine_state *from,
152                       const struct coroutine_state *to)
153 {
154         int rc;
155
156         rc = swapcontext(&from->uc, &to->uc);
157         assert(rc == 0);
158 }
159 #endif /* HAVE_UCONTEXT */