1 /* -*- Mode: c; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /*
3   libconcurrent
4   Copyright (C) 2010-2016 sharow
5 */
6 
7 #include <stdlib.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdbool.h>
11 #include <stdnoreturn.h>
12 #include <assert.h>
13 
14 #include "concurrent/concurrent.h"
15 #include "concurrent_arch.h"
16 
17 #ifndef CONCURRENT_MIN_STACK_SIZE
18 # define CONCURRENT_MIN_STACK_SIZE (1024)
19 #endif
20 
21 
22 typedef enum {
23     CONCURRENT_STATE_SETUP,
24     CONCURRENT_STATE_EXECUTE,
25     CONCURRENT_STATE_YIELD,
26     CONCURRENT_STATE_DONE
27 } concurrent_state;
28 
29 struct concurrent_ctx {
30     concurrent_proc proc;
31     void *user_ptr;
32     void *stack_orig;
33     size_t stack_size;
34     uintptr_t stack_base;
35     uintptr_t stack_ptr;
36     concurrent_state state;
37     void *caller_return_addr;
38     void *yield_value;
39     void *resume_value;
40 };
41 
42 
43 noreturn static void
panic(void)44 panic(void)
45 {
46     abort();
47 }
48 
49 static void
concurrent_setup_execution_context(struct concurrent_ctx * ctx)50 concurrent_setup_execution_context(struct concurrent_ctx *ctx)
51 {
52     ctx->stack_ptr = ctx->stack_base;
53     concurrent_arch_setup_execution_context(ctx);
54 }
55 
56 
57 void
concurrent_init(void)58 concurrent_init(void)
59 {
60     concurrent_offsetof_procedure = offsetof(struct concurrent_ctx, proc);
61     concurrent_offsetof_stack_ptr = offsetof(struct concurrent_ctx, stack_ptr);
62     concurrent_offsetof_caller_return_addr = offsetof(struct concurrent_ctx, caller_return_addr);
63 }
64 
65 void
concurrent_fin(void)66 concurrent_fin(void)
67 {
68 }
69 
70 
71 size_t
concurrent_sizeof(void)72 concurrent_sizeof(void)
73 {
74     return sizeof(struct concurrent_ctx);
75 }
76 
77 concurrent_status
concurrent_construct(struct concurrent_ctx * ctx,void * stack_buffer,size_t stack_size,concurrent_proc proc,void * user_ptr)78 concurrent_construct(struct concurrent_ctx *ctx,
79                      void *stack_buffer,
80                      size_t stack_size,
81                      concurrent_proc proc,
82                      void *user_ptr)
83 {
84     const uintptr_t align = 16 - 1;
85     uintptr_t aligned_stack_buffer;
86     if (ctx && stack_buffer && proc) {
87     } else {
88         return CONCURRENT_E_INVALID_ARGUMENT;
89     }
90     if (stack_size < CONCURRENT_MIN_STACK_SIZE) {
91         return CONCURRENT_E_STACKSIZE_TOO_SMALL;
92     }
93     aligned_stack_buffer = ((uintptr_t)stack_buffer + align) & ~align;
94     assert((uintptr_t)aligned_stack_buffer >= (uintptr_t)stack_buffer);
95     ctx->proc = proc;
96     ctx->user_ptr = user_ptr;
97     ctx->stack_orig = stack_buffer;
98     ctx->stack_size = (stack_size - (size_t)(aligned_stack_buffer - (uintptr_t)stack_buffer)) & ~(size_t)align;
99     ctx->stack_base = aligned_stack_buffer + ctx->stack_size;
100     ctx->stack_ptr = 0;
101     ctx->state = CONCURRENT_STATE_SETUP;
102     ctx->caller_return_addr = NULL;
103     ctx->yield_value = NULL;
104     ctx->resume_value = NULL;
105     return CONCURRENT_SUCCESS;
106 }
107 
108 void
concurrent_destruct(struct concurrent_ctx * ctx)109 concurrent_destruct(struct concurrent_ctx *ctx)
110 {
111     ctx->proc = NULL;
112     ctx->user_ptr = NULL;
113     ctx->stack_orig = NULL;
114     ctx->stack_size = 0;
115     ctx->stack_base = 0;
116     ctx->stack_ptr = 0;
117     ctx->state = CONCURRENT_STATE_DONE;
118     ctx->caller_return_addr = NULL;
119     ctx->yield_value = NULL;
120     ctx->resume_value = NULL;
121 }
122 
123 void *
concurrent_resume_with_value(struct concurrent_ctx * ctx,void * value)124 concurrent_resume_with_value(struct concurrent_ctx *ctx, void *value)
125 {
126     switch (ctx->state) {
127     case CONCURRENT_STATE_SETUP:
128         concurrent_setup_execution_context(ctx);
129         // fall through
130 
131     case CONCURRENT_STATE_YIELD:
132         ctx->state = CONCURRENT_STATE_EXECUTE;
133         ctx->yield_value = NULL;
134         ctx->resume_value = value;
135         concurrent_arch_trampoline_to_procedure(ctx);
136         if (ctx->state == CONCURRENT_STATE_EXECUTE) {
137             // not yeild
138             ctx->state = CONCURRENT_STATE_DONE;
139             return NULL;
140         } else {
141             return ctx->yield_value;
142         }
143     case CONCURRENT_STATE_EXECUTE:
144     case CONCURRENT_STATE_DONE:
145     default:
146         panic();
147     }
148     return NULL;
149 }
150 
151 void *
concurrent_resume(struct concurrent_ctx * ctx)152 concurrent_resume(struct concurrent_ctx *ctx)
153 {
154     return concurrent_resume_with_value(ctx, NULL);
155 }
156 
157 void *
concurrent_get_resume_value(struct concurrent_ctx * ctx)158 concurrent_get_resume_value(struct concurrent_ctx *ctx)
159 {
160     return ctx->resume_value;
161 }
162 
163 void *
concurrent_yield_with_value(struct concurrent_ctx * ctx,void * value)164 concurrent_yield_with_value(struct concurrent_ctx *ctx, void *value)
165 {
166     if (ctx->state != CONCURRENT_STATE_EXECUTE) {
167         panic();
168     }
169     ctx->yield_value = value;
170     ctx->state = CONCURRENT_STATE_YIELD;
171     concurrent_arch_trampoline_to_caller(ctx);
172     return ctx->resume_value;
173 }
174 
175 void *
concurrent_yield(struct concurrent_ctx * ctx)176 concurrent_yield(struct concurrent_ctx *ctx)
177 {
178     return concurrent_yield_with_value(ctx, NULL);
179 }
180 
181 void *
concurrent_get_yield_value(struct concurrent_ctx * ctx)182 concurrent_get_yield_value(struct concurrent_ctx *ctx)
183 {
184     return ctx->yield_value;
185 }
186 
187 void
concurrent_reset(struct concurrent_ctx * ctx)188 concurrent_reset(struct concurrent_ctx *ctx)
189 {
190     if (ctx->state == CONCURRENT_STATE_EXECUTE) {
191         panic();
192     }
193     ctx->state = CONCURRENT_STATE_SETUP;
194 }
195 
196 void *
concurrent_get_user_ptr(struct concurrent_ctx * ctx)197 concurrent_get_user_ptr(struct concurrent_ctx *ctx)
198 {
199     return ctx->user_ptr;
200 }
201 
202 bool
concurrent_is_done(struct concurrent_ctx * ctx)203 concurrent_is_done(struct concurrent_ctx *ctx)
204 {
205     return (ctx->state == CONCURRENT_STATE_DONE) ? true : false;
206 }
207 
208 size_t
concurrent_get_stack_used(struct concurrent_ctx * ctx)209 concurrent_get_stack_used(struct concurrent_ctx *ctx)
210 {
211     if (ctx->state == CONCURRENT_STATE_SETUP) {
212         return 0;
213     }
214     if (ctx->state == CONCURRENT_STATE_EXECUTE) {
215         panic();
216     }
217     return (size_t)(ctx->stack_base - ctx->stack_ptr);
218 }
219 
220 void *
concurrent_get_stack(struct concurrent_ctx * ctx)221 concurrent_get_stack(struct concurrent_ctx *ctx)
222 {
223     return ctx->stack_orig;
224 }
225