1 | /* |
2 | Copyright 2011 Kristian Nielsen and Monty Program Ab |
3 | |
4 | This file is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | Simple API for spawning a co-routine, to be used for async libmysqlclient. |
20 | |
21 | Idea is that by implementing this interface using whatever facilities are |
22 | available for given platform, we can use the same code for the generic |
23 | libmysqlclient-async code. |
24 | |
25 | (This particular implementation uses Posix ucontext swapcontext().) |
26 | */ |
27 | |
28 | #ifdef __WIN__ |
29 | #define MY_CONTEXT_USE_WIN32_FIBERS 1 |
30 | #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__x86_64__) && !defined(__ILP32__) |
31 | #define MY_CONTEXT_USE_X86_64_GCC_ASM |
32 | #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__) |
33 | #define MY_CONTEXT_USE_I386_GCC_ASM |
34 | #elif defined(HAVE_UCONTEXT_H) |
35 | #define MY_CONTEXT_USE_UCONTEXT |
36 | #else |
37 | #define MY_CONTEXT_DISABLE |
38 | #endif |
39 | |
40 | #ifdef MY_CONTEXT_USE_WIN32_FIBERS |
41 | struct my_context { |
42 | void (*user_func)(void *); |
43 | void *user_arg; |
44 | void *app_fiber; |
45 | void *lib_fiber; |
46 | int return_value; |
47 | #ifndef DBUG_OFF |
48 | void *dbug_state; |
49 | #endif |
50 | }; |
51 | #endif |
52 | |
53 | |
54 | #ifdef MY_CONTEXT_USE_UCONTEXT |
55 | #include <ucontext.h> |
56 | |
57 | struct my_context { |
58 | void (*user_func)(void *); |
59 | void *user_data; |
60 | void *stack; |
61 | size_t stack_size; |
62 | ucontext_t base_context; |
63 | ucontext_t spawned_context; |
64 | int active; |
65 | #ifdef HAVE_VALGRIND_MEMCHECK_H |
66 | unsigned int valgrind_stack_id; |
67 | #endif |
68 | #ifndef DBUG_OFF |
69 | void *dbug_state; |
70 | #endif |
71 | }; |
72 | #endif |
73 | |
74 | |
75 | #ifdef MY_CONTEXT_USE_X86_64_GCC_ASM |
76 | #include <stdint.h> |
77 | |
78 | struct my_context { |
79 | uint64_t save[9]; |
80 | void *stack_top; |
81 | void *stack_bot; |
82 | #ifdef HAVE_VALGRIND_MEMCHECK_H |
83 | unsigned int valgrind_stack_id; |
84 | #endif |
85 | #ifndef DBUG_OFF |
86 | void *dbug_state; |
87 | #endif |
88 | }; |
89 | #endif |
90 | |
91 | |
92 | #ifdef MY_CONTEXT_USE_I386_GCC_ASM |
93 | #include <stdint.h> |
94 | |
95 | struct my_context { |
96 | uint64_t save[7]; |
97 | void *stack_top; |
98 | void *stack_bot; |
99 | #ifdef HAVE_VALGRIND_MEMCHECK_H |
100 | unsigned int valgrind_stack_id; |
101 | #endif |
102 | #ifndef DBUG_OFF |
103 | void *dbug_state; |
104 | #endif |
105 | }; |
106 | #endif |
107 | |
108 | |
109 | #ifdef MY_CONTEXT_DISABLE |
110 | struct my_context { |
111 | int dummy; |
112 | }; |
113 | #endif |
114 | |
115 | |
116 | /* |
117 | Initialize an asynchroneous context object. |
118 | Returns 0 on success, non-zero on failure. |
119 | */ |
120 | extern int my_context_init(struct my_context *c, size_t stack_size); |
121 | |
122 | /* Free an asynchroneous context object, deallocating any resources used. */ |
123 | extern void my_context_destroy(struct my_context *c); |
124 | |
125 | /* |
126 | Spawn an asynchroneous context. The context will run the supplied user |
127 | function, passing the supplied user data pointer. |
128 | |
129 | The context must have been initialised with my_context_init() prior to |
130 | this call. |
131 | |
132 | The user function may call my_context_yield(), which will cause this |
133 | function to return 1. Then later my_context_continue() may be called, which |
134 | will resume the asynchroneous context by returning from the previous |
135 | my_context_yield() call. |
136 | |
137 | When the user function returns, this function returns 0. |
138 | |
139 | In case of error, -1 is returned. |
140 | */ |
141 | extern int my_context_spawn(struct my_context *c, void (*f)(void *), void *d); |
142 | |
143 | /* |
144 | Suspend an asynchroneous context started with my_context_spawn. |
145 | |
146 | When my_context_yield() is called, execution immediately returns from the |
147 | last my_context_spawn() or my_context_continue() call. Then when later |
148 | my_context_continue() is called, execution resumes by returning from this |
149 | my_context_yield() call. |
150 | |
151 | Returns 0 if ok, -1 in case of error. |
152 | */ |
153 | extern int my_context_yield(struct my_context *c); |
154 | |
155 | /* |
156 | Resume an asynchroneous context. The context was spawned by |
157 | my_context_spawn(), and later suspended inside my_context_yield(). |
158 | |
159 | The asynchroneous context may be repeatedly suspended with |
160 | my_context_yield() and resumed with my_context_continue(). |
161 | |
162 | Each time it is suspended, this function returns 1. When the originally |
163 | spawned user function returns, this function returns 0. |
164 | |
165 | In case of error, -1 is returned. |
166 | */ |
167 | extern int my_context_continue(struct my_context *c); |
168 | |
169 | |
170 | struct mysql_async_context { |
171 | /* |
172 | This is set to the value that should be returned from foo_start() or |
173 | foo_cont() when a call is suspended. |
174 | */ |
175 | unsigned int events_to_wait_for; |
176 | /* |
177 | It is also set to the event(s) that triggered when a suspended call is |
178 | resumed, eg. whether we woke up due to connection completed or timeout |
179 | in mysql_real_connect_cont(). |
180 | */ |
181 | unsigned int events_occurred; |
182 | /* |
183 | This is set to the result of the whole asynchronous operation when it |
184 | completes. It uses a union, as different calls have different return |
185 | types. |
186 | */ |
187 | union { |
188 | void *r_ptr; |
189 | const void *r_const_ptr; |
190 | int r_int; |
191 | my_bool r_my_bool; |
192 | } ret_result; |
193 | /* |
194 | The timeout value (in millisecods), for suspended calls that need to wake |
195 | up on a timeout (eg. mysql_real_connect_start(). |
196 | */ |
197 | unsigned int timeout_value; |
198 | /* |
199 | This flag is set when we are executing inside some asynchronous call |
200 | foo_start() or foo_cont(). It is used to decide whether to use the |
201 | synchronous or asynchronous version of calls that may block such as |
202 | recv(). |
203 | |
204 | Note that this flag is not set when a call is suspended, eg. after |
205 | returning from foo_start() and before re-entering foo_cont(). |
206 | */ |
207 | my_bool active; |
208 | /* |
209 | This flag is set when an asynchronous operation is in progress, but |
210 | suspended. Ie. it is set when foo_start() or foo_cont() returns because |
211 | the operation needs to block, suspending the operation. |
212 | |
213 | It is used to give an error (rather than crash) if the application |
214 | attempts to call some foo_cont() method when no suspended operation foo is |
215 | in progress. |
216 | */ |
217 | my_bool suspended; |
218 | /* |
219 | If non-NULL, this is a pointer to a callback hook that will be invoked with |
220 | the user data argument just before the context is suspended, and just after |
221 | it is resumed. |
222 | */ |
223 | void (*suspend_resume_hook)(my_bool suspend, void *user_data); |
224 | void *suspend_resume_hook_user_data; |
225 | /* |
226 | This is used to save the execution contexts so that we can suspend an |
227 | operation and switch back to the application context, to resume the |
228 | suspended context later when the application re-invokes us with |
229 | foo_cont(). |
230 | */ |
231 | struct my_context async_context; |
232 | }; |
233 | |