1 /* Copyright (C) 2014 InfiniDB, Inc.
2    Copyright (C) 2016 MariaDB Corporation
3 
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License
6    as published by the Free Software Foundation; version 2 of
7    the License.
8 
9    This program 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17    MA 02110-1301, USA. */
18 
19 /*****************************************************************************
20  * $Id$
21  *
22  ****************************************************************************/
23 
24 /** @file
25  * This file contains the longer SessionManager stress tests.
26  */
27 
28 #include <string>
29 #include <typeinfo>
30 #include <sstream>
31 #include <stdexcept>
32 #include <iostream>
33 #include <unistd.h>
34 #include <cstdlib>
35 #include <values.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include "calpontsystemcatalog.h"
39 #include <sys/types.h>
40 #include <sys/ipc.h>
41 #include <sys/sem.h>
42 #include <sys/shm.h>
43 #include "sessionmanager.h"
44 #include <cppunit/extensions/HelperMacros.h>
45 #include <boost/thread.hpp>
46 
47 using namespace std;
48 using namespace execplan;
49 
50 int threadStop, threadNum;
51 
SMRunner(void * arg)52 static void* SMRunner(void* arg)
53 {
54     SessionManager* sm;
55     int op;
56     uint32_t seed, sessionnum;
57     SessionManager::TxnID tmp;
58     int tNum = reinterpret_cast<int>(arg);
59 
60     struct entry
61     {
62         SessionManager::TxnID tid;
63         uint32_t sessionnum;
64         struct entry* next;
65     };
66 
67     struct entry* e;
68     struct entry* listHead = NULL;
69     struct entry* listTail = NULL;
70 
71     cerr << "Thread " << tNum << " started." << endl;
72 
73     seed = time(NULL) % MAXINT;
74 
75     sm = new SessionManager();
76 
77     while (!threadStop)
78     {
79         sm->verifySize();
80         op = rand_r(&seed) % 4;  // 0 = newTxnID, 1 = committed, 2 = getTxnID, 3 = delete sm; new sm
81         sessionnum = rand_r(&seed);
82 
83         switch (op)
84         {
85             case 0:
86                 e = new struct entry;
87                 e->tid = sm->newTxnID(sessionnum, false);
88 
89                 if (e->tid.valid == false)     // SM is full
90                 {
91                     delete e;
92                     break;
93                 }
94 
95                 e->sessionnum = sessionnum;
96                 e->next = NULL;
97 
98                 if (listTail != NULL)
99                     listTail->next = e;
100                 else
101                     listHead = e;
102 
103                 listTail = e;
104                 break;
105 
106             case 1:
107                 if (listHead == NULL)
108                     continue;
109 
110                 sm->committed(listHead->tid);
111                 e = listHead;
112                 listHead = listHead->next;
113 
114                 if (listHead == NULL)
115                     listTail = NULL;
116 
117                 delete e;
118                 break;
119 
120             case 2:
121                 if (listHead == NULL)
122                     continue;
123 
124                 tmp = sm->getTxnID(listHead->sessionnum);
125                 CPPUNIT_ASSERT(tmp.valid == listHead->tid.valid == true);
126                 // there's some risk of collision here if 2 threads happen to choose the
127                 // same session number
128                 //CPPUNIT_ASSERT(tmp.id == listHead->tid.id);
129                 break;
130 
131             case 3:
132                 delete sm;
133                 sm = new SessionManager();
134                 break;
135 
136             default:
137                 cerr << "SMRunner: ??" << endl;
138         };
139     }
140 
141     while (listHead != NULL)
142     {
143         e = listHead;
144         listHead = listHead->next;
145         delete e;
146     }
147 
148     delete sm;
149     cerr << "Thread " << tNum << " exiting." << endl;
150     pthread_exit(0);
151 }
152 
153 class ExecPlanTest : public CppUnit::TestFixture
154 {
155 
156     CPPUNIT_TEST_SUITE( ExecPlanTest );
157     CPPUNIT_TEST(sessionManager_3);
158     unlink("/tmp/CalpontShm");
159     CPPUNIT_TEST_SUITE_END();
160 
161 
162 public:
163 
164     /*
165     * destroySemaphores() and destroyShmseg() will print error messages
166     * if there are no objects to destroy.  That's OK.
167     */
destroySemaphores()168     void destroySemaphores()
169     {
170         key_t semkey;
171         char* semseed = "/usr/local/mariadb/columnstore/etc/Columnstore.xml";
172         int sems, err;
173 
174 // 		semkey = ftok(semseed, 0x2149bdd2);   // these things must match in the SM constructor
175         semkey = 0x2149bdd2;
176 
177         if (semkey == -1)
178             perror("tdriver: ftok");
179 
180         sems = semget(semkey, 2, 0666);
181 
182         if (sems != -1)
183         {
184             err = semctl(sems, 0, IPC_RMID);
185 
186             if (err == -1)
187                 perror("tdriver: semctl");
188         }
189     }
190 
destroyShmseg()191     void destroyShmseg()
192     {
193         key_t shmkey;
194         char* shmseed = "/usr/local/mariadb/columnstore/etc/Columnstore.xml";
195         int shms, err;
196 
197 // 		shmkey = ftok(shmseed, 0x2149bdd2);   // these things much match in the SM constructor
198         shmkey = 0x2149bdd2;
199 
200         if (shmkey == -1)
201             perror("tdriver: ftok");
202 
203         shms = shmget(shmkey, 0, 0666);
204 
205         if (shms != -1)
206         {
207             err = shmctl(shms, IPC_RMID, NULL);
208 
209             if (err == -1 && errno != EINVAL)
210             {
211                 perror("tdriver: shmctl");
212                 return;
213             }
214         }
215     }
216 
217     /** This launches several threads to stress test the Session Manager
218     */
sessionManager_3()219     void sessionManager_3()
220     {
221         const int threadCount = 4;
222         int i;
223         pthread_t threads[threadCount];
224 
225         cerr << endl << "Multithreaded SessionManager test.  "
226              "This runs for 2 minutes." << endl;
227 
228         destroySemaphores();
229         destroyShmseg();
230         unlink("/tmp/CalpontShm");
231 
232         threadStop = 0;
233 
234         for (i = 0; i < threadCount; i++)
235         {
236             if (pthread_create(&threads[i], NULL, SMRunner,
237                                reinterpret_cast<void*>(i + 1)) < 0)
238                 throw logic_error("Error creating threads for the Session Manager test");
239 
240             usleep(1000);
241         }
242 
243         sleep(120);
244         threadStop = 1;
245 
246         for (i = 0; i < threadCount; i++)
247             pthread_join(threads[i], NULL);
248 
249         destroySemaphores();
250         destroyShmseg();
251     }
252 
253 
254 };
255 
256 CPPUNIT_TEST_SUITE_REGISTRATION( ExecPlanTest );
257 
258 #include <cppunit/extensions/TestFactoryRegistry.h>
259 #include <cppunit/ui/text/TestRunner.h>
260 
main(int argc,char ** argv)261 int main( int argc, char** argv)
262 {
263     CppUnit::TextUi::TestRunner runner;
264     CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
265     runner.addTest( registry.makeTest() );
266     bool wasSuccessful = runner.run( "", false );
267     return (wasSuccessful ? 0 : 1);
268 }
269