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 _WIN32
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
41struct 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
57struct 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
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
78struct my_context {
79 uint64_t save[9];
80 void *stack_top;
81 void *stack_bot;
82#ifdef HAVE_VALGRIND
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
95struct my_context {
96 uint64_t save[7];
97 void *stack_top;
98 void *stack_bot;
99#ifdef HAVE_VALGRIND
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
110struct my_context {
111 int dummy;
112};
113#endif
114
115/*
116 Initialize an asynchroneous context object.
117 Returns 0 on success, non-zero on failure.
118*/
119extern int my_context_init(struct my_context *c, size_t stack_size);
120
121/* Free an asynchroneous context object, deallocating any resources used. */
122extern void my_context_destroy(struct my_context *c);
123
124/*
125 Spawn an asynchroneous context. The context will run the supplied user
126 function, passing the supplied user data pointer.
127
128 The context must have been initialised with my_context_init() prior to
129 this call.
130
131 The user function may call my_context_yield(), which will cause this
132 function to return 1. Then later my_context_continue() may be called, which
133 will resume the asynchroneous context by returning from the previous
134 my_context_yield() call.
135
136 When the user function returns, this function returns 0.
137
138 In case of error, -1 is returned.
139*/
140extern int my_context_spawn(struct my_context *c, void (*f)(void *), void *d);
141
142/*
143 Suspend an asynchroneous context started with my_context_spawn.
144
145 When my_context_yield() is called, execution immediately returns from the
146 last my_context_spawn() or my_context_continue() call. Then when later
147 my_context_continue() is called, execution resumes by returning from this
148 my_context_yield() call.
149
150 Returns 0 if ok, -1 in case of error.
151*/
152extern int my_context_yield(struct my_context *c);
153
154/*
155 Resume an asynchroneous context. The context was spawned by
156 my_context_spawn(), and later suspended inside my_context_yield().
157
158 The asynchroneous context may be repeatedly suspended with
159 my_context_yield() and resumed with my_context_continue().
160
161 Each time it is suspended, this function returns 1. When the originally
162 spawned user function returns, this function returns 0.
163
164 In case of error, -1 is returned.
165*/
166extern int my_context_continue(struct my_context *c);
167
168struct st_ma_pvio;
169
170struct 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_occured;
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 struct st_ma_pvio *pvio;
224 void (*suspend_resume_hook)(my_bool suspend, void *user_data);
225 void *suspend_resume_hook_user_data;
226 /*
227 This is used to save the execution contexts so that we can suspend an
228 operation and switch back to the application context, to resume the
229 suspended context later when the application re-invokes us with
230 foo_cont().
231 */
232 struct my_context async_context;
233};
234