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