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 02110-1335 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 
38 class 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 */
45 class Apc_target
46 {
47   mysql_mutex_t *LOCK_thd_kill_ptr;
48 public:
Apc_target()49   Apc_target() : enabled(0), apc_calls(NULL) {}
~Apc_target()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. */
destroy()55   void destroy() { DBUG_ASSERT(!enabled); }
56 
57   /* Enter ther state where the target is available for serving APC requests */
enable()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   */
disable()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   */
have_apc_requests()82   inline bool have_apc_requests()
83   {
84     return MY_TEST(apc_calls);
85   }
86 
is_enabled()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;
~Apc_call()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
104 private:
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 */
get_first_in_queue()148   Call_request *get_first_in_queue()
149   {
150     return apc_calls;
151   }
152 };
153 
154 #ifdef HAVE_PSI_INTERFACE
155 void 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