1#ifndef SQL_MY_APC_INCLUDED
2#define SQL_MY_APC_INCLUDED
3/*
4 Copyright (c) 2011, 2013 Monty Program Ab.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; version 2 of the License.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
18
19/*
20 Interface
21 ~~~~~~~~~
22 (
23 - This is an APC request queue
24 - We assume there is a particular owner thread which periodically calls
25 process_apc_requests() to serve the call requests.
26 - Other threads can post call requests, and block until they are exectued.
27 )
28
29 Implementation
30 ~~~~~~~~~~~~~~
31 - The target has a mutex-guarded request queue.
32
33 - After the request has been put into queue, the requestor waits for request
34 to be satisfied. The worker satisifes the request and signals the
35 requestor.
36*/
37
38class THD;
39
40/*
41 Target for asynchronous procedure calls (APCs).
42 - A target is running in some particular thread,
43 - One can make calls to it from other threads.
44*/
45class Apc_target
46{
47 mysql_mutex_t *LOCK_thd_kill_ptr;
48public:
49 Apc_target() : enabled(0), apc_calls(NULL) {}
50 ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
51
52 void init(mysql_mutex_t *target_mutex);
53
54 /* Destroy the target. The target must be disabled when this call is made. */
55 void destroy() { DBUG_ASSERT(!enabled); }
56
57 /* Enter ther state where the target is available for serving APC requests */
58 void enable() { enabled++; }
59
60 /*
61 Make the target unavailable for serving APC requests.
62
63 @note
64 This call will serve all requests that were already enqueued
65 */
66 void disable()
67 {
68 DBUG_ASSERT(enabled);
69 mysql_mutex_lock(LOCK_thd_kill_ptr);
70 bool process= !--enabled && have_apc_requests();
71 mysql_mutex_unlock(LOCK_thd_kill_ptr);
72 if (unlikely(process))
73 process_apc_requests();
74 }
75
76 void process_apc_requests();
77 /*
78 A lightweight function, intended to be used in frequent checks like this:
79
80 if (apc_target.have_requests()) apc_target.process_apc_requests()
81 */
82 inline bool have_apc_requests()
83 {
84 return MY_TEST(apc_calls);
85 }
86
87 inline bool is_enabled() { return enabled; }
88
89 /* Functor class for calls you can schedule */
90 class Apc_call
91 {
92 public:
93 /* This function will be called in the target thread */
94 virtual void call_in_target_thread()= 0;
95 virtual ~Apc_call() {}
96 };
97
98 /* Make a call in the target thread (see function definition for details) */
99 bool make_apc_call(THD *caller_thd, Apc_call *call, int timeout_sec, bool *timed_out);
100
101#ifndef DBUG_OFF
102 int n_calls_processed; /* Number of calls served by this target */
103#endif
104private:
105 class Call_request;
106
107 /*
108 Non-zero value means we're enabled. It's an int, not bool, because one can
109 call enable() N times (and then needs to call disable() N times before the
110 target is really disabled)
111 */
112 int enabled;
113
114 /*
115 Circular, double-linked list of all enqueued call requests.
116 We use this structure, because we
117 - process requests sequentially: requests are added at the end of the
118 list and removed from the front. With circular list, we can keep one
119 pointer, and access both front an back of the list with it.
120 - a thread that has posted a request may time out (or be KILLed) and
121 cancel the request, which means we need a fast request-removal
122 operation.
123 */
124 Call_request *apc_calls;
125
126 class Call_request
127 {
128 public:
129 Apc_call *call; /* Functor to be called */
130
131 /* The caller will actually wait for "processed==TRUE" */
132 bool processed;
133
134 /* Condition that will be signalled when the request has been served */
135 mysql_cond_t COND_request;
136
137 /* Double linked-list linkage */
138 Call_request *next;
139 Call_request *prev;
140
141 const char *what; /* (debug) state of the request */
142 };
143
144 void enqueue_request(Call_request *qe);
145 void dequeue_request(Call_request *qe);
146
147 /* return the first call request in queue, or NULL if there are none enqueued */
148 Call_request *get_first_in_queue()
149 {
150 return apc_calls;
151 }
152};
153
154#ifdef HAVE_PSI_INTERFACE
155void init_show_explain_psi_keys(void);
156#else
157#define init_show_explain_psi_keys() /* no-op */
158#endif
159
160#endif //SQL_MY_APC_INCLUDED
161
162