1/*
2 Copyright (c) 2012, Monty Program Ab
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
16
17/*
18 This file does standalone APC system tests.
19*/
20#include <my_global.h>
21#include <my_pthread.h>
22#include <my_sys.h>
23
24#include <stdio.h>
25
26#include <tap.h>
27
28/*
29 A fake THD with enter_cond/exit_cond and some other members.
30*/
31PSI_stage_info stage_show_explain;
32class THD
33{
34 mysql_mutex_t* thd_mutex;
35public:
36 bool killed;
37
38 THD() : killed(FALSE) {}
39 inline const char* ENTER_COND(mysql_cond_t *cond, mysql_mutex_t* mutex,
40 PSI_stage_info*, PSI_stage_info*)
41 {
42 mysql_mutex_assert_owner(mutex);
43 thd_mutex= mutex;
44 return NULL;
45 }
46 inline void EXIT_COND(PSI_stage_info*)
47 {
48 mysql_mutex_unlock(thd_mutex);
49 }
50};
51
52#include "../sql/my_apc.h"
53
54#define MY_APC_STANDALONE 1
55#include "../sql/my_apc.cc"
56
57volatile bool started= FALSE;
58volatile bool service_should_exit= FALSE;
59volatile bool requestors_should_exit=FALSE;
60
61/* Counters for APC calls */
62int apcs_served= 0;
63int apcs_missed=0;
64int apcs_timed_out=0;
65mysql_mutex_t apc_counters_mutex;
66
67inline void increment_counter(int *var)
68{
69 mysql_mutex_lock(&apc_counters_mutex);
70 *var= *var+1;
71 mysql_mutex_unlock(&apc_counters_mutex);
72}
73
74volatile bool have_errors= false;
75
76Apc_target apc_target;
77mysql_mutex_t target_mutex;
78
79int int_rand(int size)
80{
81 return (int) (0.5 + ((double)rand() / RAND_MAX) * size);
82}
83
84/*
85 APC target thread (the one that will serve the APC requests). We will have
86 one target.
87*/
88void *test_apc_service_thread(void *ptr)
89{
90 my_thread_init();
91 mysql_mutex_init(0, &target_mutex, MY_MUTEX_INIT_FAST);
92 apc_target.init(&target_mutex);
93 apc_target.enable();
94 started= TRUE;
95 diag("test_apc_service_thread started");
96 while (!service_should_exit)
97 {
98 //apc_target.disable();
99 my_sleep(10000);
100 //apc_target.enable();
101 for (int i = 0; i < 10 && !service_should_exit; i++)
102 {
103 apc_target.process_apc_requests();
104 my_sleep(int_rand(30));
105 }
106 }
107 apc_target.disable();
108 apc_target.destroy();
109 mysql_mutex_destroy(&target_mutex);
110 my_thread_end();
111 pthread_exit(0);
112 return NULL;
113}
114
115
116/*
117 One APC request (to write 'value' into *where_to)
118*/
119class Apc_order : public Apc_target::Apc_call
120{
121public:
122 int value; // The value
123 int *where_to; // Where to write it
124 Apc_order(int a, int *b) : value(a), where_to(b) {}
125
126 void call_in_target_thread()
127 {
128 my_sleep(int_rand(1000));
129 *where_to = value;
130 increment_counter(&apcs_served);
131 }
132};
133
134
135/*
136 APC requestor thread. It makes APC requests, and checks if they were actually
137 executed.
138*/
139void *test_apc_requestor_thread(void *ptr)
140{
141 my_thread_init();
142 diag("test_apc_requestor_thread started");
143 THD my_thd;
144
145 while (!requestors_should_exit)
146 {
147 int dst_value= 0;
148 int src_value= int_rand(4*1000*100);
149 /* Create an APC to do "dst_value= src_value" assignment */
150 Apc_order apc_order(src_value, &dst_value);
151 bool timed_out;
152
153 mysql_mutex_lock(&target_mutex);
154 bool res= apc_target.make_apc_call(&my_thd, &apc_order, 60, &timed_out);
155 if (res)
156 {
157 if (timed_out)
158 increment_counter(&apcs_timed_out);
159 else
160 increment_counter(&apcs_missed);
161
162 if (dst_value != 0)
163 {
164 diag("APC was done even though return value says it wasnt!");
165 have_errors= true;
166 }
167 }
168 else
169 {
170 if (dst_value != src_value)
171 {
172 diag("APC was not done even though return value says it was!");
173 have_errors= true;
174 }
175 }
176 //my_sleep(300);
177 }
178 diag("test_apc_requestor_thread exiting");
179 my_thread_end();
180 return NULL;
181}
182
183/* Number of APC requestor threads */
184const int N_THREADS=23;
185
186
187int main(int args, char **argv)
188{
189 pthread_t service_thr;
190 pthread_t request_thr[N_THREADS];
191 int i;
192
193 my_thread_global_init();
194
195 mysql_mutex_init(0, &apc_counters_mutex, MY_MUTEX_INIT_FAST);
196
197 plan(1);
198 diag("Testing APC delivery and execution");
199
200 pthread_create(&service_thr, NULL, test_apc_service_thread, (void*)NULL);
201 while (!started)
202 my_sleep(1000);
203 for (i = 0; i < N_THREADS; i++)
204 pthread_create(&request_thr[i], NULL, test_apc_requestor_thread, (void*)NULL);
205
206 for (i = 0; i < 15; i++)
207 {
208 my_sleep(500*1000);
209 diag("%d APCs served %d missed", apcs_served, apcs_missed);
210 }
211 diag("Shutting down requestors");
212 requestors_should_exit= TRUE;
213 for (i = 0; i < N_THREADS; i++)
214 pthread_join(request_thr[i], NULL);
215
216 diag("Shutting down service");
217 service_should_exit= TRUE;
218 pthread_join(service_thr, NULL);
219
220 mysql_mutex_destroy(&apc_counters_mutex);
221
222 diag("Done");
223 my_thread_end();
224 my_thread_global_end();
225
226 ok1(!have_errors);
227 return exit_status();
228}
229
230