]> git.ozlabs.org Git - ccan/blob - ccan/coroutine/coroutine.c
60ba41691bc6dbf8e722873ccfa3297c8d9ad4a1
[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 struct coroutine_stack {
18         uint64_t magic;
19         size_t size;
20 };
21
22 /* Returns lowest stack addres, regardless of growth direction */
23 static UNNEEDED void *coroutine_stack_base(struct coroutine_stack *stack)
24 {
25 #if HAVE_STACK_GROWS_UPWARDS
26         return (char *)(stack + 1);
27 #else
28         return (char *)stack - stack->size;
29 #endif
30 }
31
32 struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
33                                              size_t metasize)
34 {
35         struct coroutine_stack *stack;
36
37         BUILD_ASSERT(COROUTINE_STK_OVERHEAD == sizeof(*stack));
38 #ifdef MINSIGSTKSZ
39         BUILD_ASSERT(COROUTINE_MIN_STKSZ >= MINSIGSTKSZ);
40 #endif
41
42         if (bufsize < (COROUTINE_MIN_STKSZ + sizeof(*stack) + metasize))
43                 return NULL;
44
45 #if HAVE_STACK_GROWS_UPWARDS
46         stack = (char *)buf + metasize;
47 #else
48         stack = (struct coroutine_stack *)
49                 ((char *)buf + bufsize - metasize) - 1;
50 #endif
51
52         stack->magic = COROUTINE_STACK_MAGIC;
53         stack->size = bufsize - sizeof(*stack) - metasize;
54
55         return stack;
56 }
57
58 void coroutine_stack_release(struct coroutine_stack *stack, size_t metasize)
59 {
60         memset(stack, 0, sizeof(*stack));
61 }
62
63 struct coroutine_stack *coroutine_stack_check(struct coroutine_stack *stack,
64                                               const char *abortstr)
65 {
66         if (stack && (stack->magic == COROUTINE_STACK_MAGIC)
67             && (stack->size >= COROUTINE_MIN_STKSZ))
68                 return stack;
69
70         if (abortstr) {
71                 if (!stack)
72                         fprintf(stderr, "%s: NULL coroutine stack\n", abortstr);
73                 else
74                         fprintf(stderr,
75                                 "%s: Bad coroutine stack at %p (magic=0x%"PRIx64" size=%zd)\n",
76                                 abortstr, stack, stack->magic, stack->size);
77                 abort();
78         }
79         return NULL;
80 }
81
82 size_t coroutine_stack_size(const struct coroutine_stack *stack)
83 {
84         return stack->size;
85 }
86
87 #if HAVE_UCONTEXT
88 static void coroutine_uc_stack(stack_t *uc_stack,
89                                const struct coroutine_stack *stack)
90 {
91         uc_stack->ss_size = coroutine_stack_size(stack);
92         uc_stack->ss_sp = coroutine_stack_base((struct coroutine_stack *)stack);
93 }
94 #endif /* HAVE_UCONTEXT */
95
96 /*
97  * Coroutine switching
98  */
99
100 #if HAVE_UCONTEXT
101 void coroutine_init_(struct coroutine_state *cs,
102                      void (*fn)(void *), void *arg,
103                      struct coroutine_stack *stack)
104 {
105         getcontext (&cs->uc);
106
107         coroutine_uc_stack(&cs->uc.uc_stack, stack);
108
109         if (HAVE_POINTER_SAFE_MAKECONTEXT) {
110                 makecontext(&cs->uc, (void *)fn, 1, arg);
111         } else {
112                 ptrdiff_t si = ptr2int(arg);
113                 ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
114                 int lo = si & mask;
115                 int hi = si >> (sizeof(int) * 8);
116
117                 makecontext(&cs->uc, (void *)fn, 2, lo, hi);
118         }
119         
120 }
121
122 void coroutine_jump(const struct coroutine_state *to)
123 {
124         setcontext(&to->uc);
125         assert(0);
126 }
127
128 void coroutine_switch(struct coroutine_state *from,
129                       const struct coroutine_state *to)
130 {
131         int rc;
132
133         rc = swapcontext(&from->uc, &to->uc);
134         assert(rc == 0);
135 }
136 #endif /* HAVE_UCONTEXT */