1 /*
2 Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3 All rights reserved. Use is subject to license terms.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 /**
27 * NdbPortLibTest.cpp
28 * Test the functionality of portlib
29 * TODO - Add tests for NdbMem
30 */
31
32 #include <ndb_global.h>
33
34 #include "NdbOut.hpp"
35 #include "NdbThread.h"
36 #include "NdbMutex.h"
37 #include "NdbCondition.h"
38 #include "NdbSleep.h"
39 #include "NdbTick.h"
40 #include "NdbEnv.h"
41 #include "NdbHost.h"
42 #include "NdbMain.h"
43
44 int TestHasFailed;
45 int verbose = 0;
46
fail(const char * test,const char * cause)47 static void fail(const char* test, const char* cause)
48 {
49 TestHasFailed = 1;
50 ndbout << test << " failed, " << cause << endl;
51 }
52
53 // test 1 variables and funcs
54
thread1func(void * arg)55 extern "C" void* thread1func(void* arg)
56 {
57 int arg1;
58 int returnvalue = 8;
59 arg1 = *(int*)arg;
60 ndbout << "thread1: thread1func called with arg = " << arg1 << endl;
61
62 // delay(1000);
63 if (arg1 != 7)
64 fail("TEST1", "Wrong arg");
65
66 return (void*) returnvalue;
67 }
68
69 // test 2 variables and funcs
70
71 NdbMutex* test2mutex;
72
test2func(void * arg)73 extern "C" void* test2func(void* arg)
74 {
75
76 int arg1;
77 arg1 = *(int*)arg;
78 ndbout << "thread" << arg1 << " started in test2func" << endl;
79
80 if (NdbMutex_Lock(test2mutex) != 0)
81 fail("TEST2", "Failed to lock mutex");
82
83 ndbout << "thread" << arg1 << ", test2func " << endl;
84
85 if (NdbMutex_Unlock(test2mutex) != 0)
86 fail("TEST2", "Failed to unlock mutex");
87
88 int returnvalue = arg1;
89 return (void*) returnvalue;
90 }
91
92
93 // test 3 and 7 variables and funcs
94
95 NdbMutex* testmutex;
96 NdbCondition* testcond;
97 int testthreadsdone;
98
testfunc(void * arg)99 extern "C" void* testfunc(void* arg)
100 {
101 int tmpVar;
102 int threadno;
103
104 threadno = *(int*)arg;
105
106 ndbout << "Thread" << threadno << " started in testfunc" << endl;
107 do
108 {
109
110 if ((threadno % 2) == 0)
111 NdbSleep_SecSleep(1);
112 else
113 NdbSleep_MilliSleep(100);
114
115 if (NdbMutex_Lock(testmutex) != 0)
116 fail("TEST3", "Wrong result from NdbMutex_Lock function");
117
118 ndbout << "thread" << threadno << ", testfunc " << endl;
119 testthreadsdone++;
120 tmpVar = testthreadsdone;
121
122 if (NdbCondition_Signal(testcond) != 0)
123 fail("TEST3", "Wrong result from NdbCondition_Signal function");
124
125 if (NdbMutex_Unlock(testmutex) != 0)
126 fail("TEST3", "Wrong result from NdbMutex_Unlock function");
127
128 }
129 while(tmpVar<100);
130
131 return 0;
132 }
133
testTryLockfunc(void * arg)134 extern "C" void* testTryLockfunc(void* arg)
135 {
136 int tmpVar = 0;
137 int threadno;
138
139 threadno = *(int*)arg;
140
141 ndbout << "Thread" << threadno << " started" << endl;
142 do
143 {
144
145 if ((threadno % 2) == 0)
146 NdbSleep_SecSleep(1);
147 else
148 NdbSleep_MilliSleep(100);
149
150 if (NdbMutex_Trylock(testmutex) == 0){
151
152 ndbout << "thread" << threadno << ", testTryLockfunc locked" << endl;
153 testthreadsdone++;
154 tmpVar = testthreadsdone;
155
156 if (NdbCondition_Signal(testcond) != 0)
157 fail("TEST3", "Wrong result from NdbCondition_Signal function");
158
159 if (NdbMutex_Unlock(testmutex) != 0)
160 fail("TEST3", "Wrong result from NdbMutex_Unlock function");
161 }
162
163 }
164 while(tmpVar<100);
165
166 return 0;
167 }
168
169
170
171 void testMicros(int count);
172 Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2);
173
174 NDB_COMMAND(PortLibTest, "portlibtest", "portlibtest", "Test the portable function layer", 4096){
175
176 ndbout << "= TESTING ARGUMENT PASSING ============" << endl;
177 ndbout << "ARGC: " << argc << endl;
178 for(int i = 1; i < argc; i++){
179 ndbout << " ARGV"<<i<<": " << (char*)argv[i] << endl;
180 }
181 ndbout << endl << endl;
182
183
184 struct NdbThread* thread1var;
185 void *status = 0;
186 int arg = 7;
187
188 TestHasFailed = 0;
189 // create one thread and wait for it to return
190 ndbout << "= TEST1 ===============================" << endl;
191
192 thread1var = NdbThread_Create(thread1func, // Function
193 (void**)&arg,// Arg
194 2048, // Stacksize
195 (char*)"thread1", // Thread name
196 NDB_THREAD_PRIO_MEAN); // Thread priority
197
198
199 if(NdbThread_WaitFor(thread1var, &status) != 0)
200 fail("TEST1", "NdbThread_WaitFor failed");
201 // NOTE! thread return value is not yet used in Ndb and thus not tested(does not work)
202 //ndbout << "thread1 returned, status = " << status << endl;
203 //if (status != 8)
204 // fail("TEST1", "Wrong status");
205 ndbout << "TEST1 completed" << endl;
206
207
208 NdbThread_Destroy(&thread1var);
209
210 // Create 10 threads that will wait for a mutex before printing it's message to screen
211 ndbout << "= TEST2 ===============================" << endl;
212 #define T2_THREADS 10
213 NdbThread* threads[T2_THREADS];
214 int args[T2_THREADS];
215 void *status2 = 0;
216 test2mutex = NdbMutex_Create();
217 NdbMutex_Lock(test2mutex);
218
219 for (int i = 0; i < T2_THREADS; i++)
220 {
221 args[i] = i;
222 threads[i] = NdbThread_Create(test2func, // Function
223 (void**)&args[i],// Arg
224 2048, // Stacksize
225 (char*)"test2thread", // Thread name
226 NDB_THREAD_PRIO_MEAN); // Thread priority
227 if (threads[i] == NULL)
228 fail("TEST2", "NdbThread_Create failed");
229 }
230
231 ndbout << "All threads created" << endl;
232
233 NdbMutex_Unlock(test2mutex);
234
235 for (int i = 0; i < T2_THREADS; i++)
236 {
237 if (NdbThread_WaitFor(threads[i], &status2))
238 fail("TEST2", "NdbThread_WaitFor failed");
239
240 NdbThread_Destroy(&threads[i]);
241 // Don't test return values
242 // ndbout << "thread" << i << " returned, status = " << status2 << endl;
243 // if (status2 != i)
244 // fail("TEST2", "Wrong status");
245 }
246
247 if (NdbMutex_Lock(test2mutex) != 0)
248 fail("TEST2", "NdbMutex_Lock failed");
249 if (NdbMutex_Unlock(test2mutex) != 0)
250 fail("TEST2", "NdbMutex_Unlock failed");
251 if (NdbMutex_Destroy(test2mutex) != 0)
252 fail("TEST2", "NdbMutex_Destroy failed");
253 ndbout << "TEST2 completed" << endl;
254
255 ndbout << "= TEST3 ===============================" << endl;
256 // Create 10 threads that will by synchronised by a condition
257 // When they are awakened and have the mutex they will increment a global variable
258 #define T3_THREADS 10
259 NdbThread* t3threads[T3_THREADS];
260 int t3args[T3_THREADS];
261 void *status3 = 0;
262
263 testmutex = NdbMutex_Create();
264 testcond = NdbCondition_Create();
265 testthreadsdone = 0;
266
267 for (int i = 0; i < T3_THREADS; i++)
268 {
269 t3args[i] = i;
270 t3threads[i] = NdbThread_Create(testfunc, // Function
271 (void**)&t3args[i],// Arg
272 2048, // Stacksize
273 (char*)"test3thread", // Thread name
274 NDB_THREAD_PRIO_MEAN); // Thread priority
275 }
276
277 ndbout << "All threads created" << endl;
278
279 if (NdbMutex_Lock(testmutex) != 0)
280 fail("TEST3", "NdbMutex_Lock failed");
281
282 while (testthreadsdone < T3_THREADS*10)
283 {
284 if(NdbCondition_Wait(testcond, testmutex) != 0)
285 fail("TEST3", "NdbCondition_Wait failed");
286 ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
287 }
288 if (NdbMutex_Unlock(testmutex) != 0)
289 fail("TEST3", "NdbMutex_Unlock failed");
290
291 for (int i = 0; i < T3_THREADS; i++)
292 {
293 if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
294 fail("TEST3", "NdbThread_WaitFor failed");
295
296 NdbThread_Destroy(&t3threads[i]);
297 //ndbout << "thread" << i << " returned, status = " << status3 << endl;
298 //if (status3 != i)
299 // fail("TEST3", "Wrong status");
300 }
301
302 NdbMutex_Destroy(testmutex);
303 NdbCondition_Destroy(testcond);
304 ndbout << "TEST3 completed" << endl;
305
306 ndbout << "= TEST4 ===============================" << endl;
307 // Check tick functions
308
309 //#if 0
310
311 int sleeptimes[] = {78, 12, 199, 567, 899};
312
313
314 for (int i = 0; i < 5; i++)
315 {
316 ndbout << "*------------------------------- Measure" << i << endl;
317
318 Uint64 millisec_now;
319 Uint64 millisec_now2;
320
321 millisec_now = NdbTick_CurrentMillisecond();
322 NdbSleep_MilliSleep(sleeptimes[i]);
323 millisec_now2 = NdbTick_CurrentMillisecond();
324
325 ndbout << " Time before sleep = " << millisec_now << endl;
326 ndbout << " Time after sleep = " << millisec_now2 << endl;
327 ndbout << " Tried to sleep "<<sleeptimes[i]<<" milliseconds." << endl;
328 ndbout << " Sleep time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
329
330 }
331
332 ndbout << "TEST4 completed" << endl;
333
334 ndbout << "= TEST5 ===============================" << endl;
335 // Check NdbOut
336
337 ndbout << "Testing hex and dec functions of NdbOut" << endl;
338
339 for (int i = 0; i<= 0xFF; i++)
340 {
341 ndbout << i << "=" <<hex << i << "="<<dec << i << ", ";
342 }
343
344 ndbout << endl<< "Testing that hex is reset to dec by endl" << endl;
345 ndbout << hex << 67 << endl;
346 ndbout << 67 << endl;
347
348 ndbout << "TEST5 completed" << endl;
349
350
351 ndbout << "= TEST6 ===============================" << endl;
352 const char* theEnvHostNamePtr;
353 char buf[255];
354 char theHostHostName[256];
355 theEnvHostNamePtr = NdbEnv_GetEnv("HOSTNAME", buf, 255);
356 if(theEnvHostNamePtr == NULL)
357 fail("TEST6", "Could not get HOSTNAME from env");
358 else{
359 ndbout << "HOSTNAME from GetEnv" << theEnvHostNamePtr << endl;
360
361 NdbHost_GetHostName(theHostHostName);
362
363 ndbout << "HOSTNAME from GetHostName" <<theHostHostName << endl;
364
365 if (strcmp(theEnvHostNamePtr, theHostHostName) != 0)
366 fail("TEST6", "NdbHost_GetHostName or NdbEnv_GetEnv failed");
367 }
368
369 ndbout << "= TEST7 ===============================" << endl;
370
371 testmutex = NdbMutex_Create();
372 testcond = NdbCondition_Create();
373 testthreadsdone = 0;
374
375 for (int i = 0; i < T3_THREADS; i++)
376 {
377 t3args[i] = i;
378 t3threads[i] = NdbThread_Create(testfunc, // Function
379 (void**)&t3args[i],// Arg
380 2048, // Stacksize
381 (char*)"test7thread", // Thread name
382 NDB_THREAD_PRIO_MEAN); // Thread priority
383 }
384
385 ndbout << "All threads created" << endl;
386
387 if (NdbMutex_Lock(testmutex) != 0)
388 fail("TEST7", "NdbMutex_Lock failed");
389
390 while (testthreadsdone < T3_THREADS*10)
391 {
392 // just testing the functionality without timing out, therefor 20 sec.
393 if(NdbCondition_WaitTimeout(testcond, testmutex, 20000) != 0)
394 fail("TEST7", "NdbCondition_WaitTimeout failed");
395 ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
396 }
397 if (NdbMutex_Unlock(testmutex) != 0)
398 fail("TEST7", "NdbMutex_Unlock failed");
399
400 for (int i = 0; i < T3_THREADS; i++)
401 {
402 if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
403 fail("TEST7", "NdbThread_WaitFor failed");
404
405 NdbThread_Destroy(&t3threads[i]);
406 }
407
408 NdbMutex_Destroy(testmutex);
409 NdbCondition_Destroy(testcond);
410
411 ndbout << "TEST7 completed" << endl;
412
413
414 ndbout << "= TEST8 ===============================" << endl;
415 ndbout << " NdbCondition_WaitTimeout" << endl;
416 testmutex = NdbMutex_Create();
417 testcond = NdbCondition_Create();
418
419 for (int i = 0; i < 5; i++)
420 {
421 ndbout << "*------------------------------- Measure" << i << endl;
422
423 Uint64 millisec_now;
424 Uint64 millisec_now2;
425
426 millisec_now = NdbTick_CurrentMillisecond();
427 if (NdbCondition_WaitTimeout(testcond, testmutex, sleeptimes[i]) != 0)
428 fail("TEST8", "NdbCondition_WaitTimeout failed");
429 millisec_now2 = NdbTick_CurrentMillisecond();
430
431 ndbout << " Time before WaitTimeout = " << millisec_now << endl;
432 ndbout << " Time after WaitTimeout = " << millisec_now2 << endl;
433 ndbout << " Tried to wait "<<sleeptimes[i]<<" milliseconds." << endl;
434 ndbout << " Wait time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
435
436 }
437
438 ndbout << "TEST8 completed" << endl;
439
440
441 ndbout << "= TEST9 ===============================" << endl;
442 ndbout << " NdbTick_CurrentXXXXXsecond compare" << endl;
443
444 for (int i = 0; i < 5; i++)
445 {
446 ndbout << "*------------------------------- Measure" << i << endl;
447
448 const NDB_TICKS t1 = NdbTick_getCurrentTicks();
449 NdbSleep_MilliSleep(sleeptimes[i]);
450 const NDB_TICKS t2 = NdbTick_getCurrentTicks();
451
452 Uint64 usecdiff = NdbTick_Elapsed(t1,t2).microSec();
453 Uint64 msecdiff = NdbTick_Elapsed(t1,t2).milliSec();
454
455 ndbout << " Slept "<<sleeptimes[i]<<" milliseconds." << endl;
456 ndbout << " Measured " << msecdiff <<" milliseconds with milli function ." << endl;
457 ndbout << " Measured " << usecdiff/1000 << "," << usecdiff%1000<<" milliseconds with micro function ." << endl;
458 }
459
460 ndbout << "TEST9 completed" << endl;
461
462
463 const int iter = 20;
464 ndbout << "Testing microsecond timer - " << iter << " iterations" << endl;
465 testMicros(iter);
466 ndbout << "Testing microsecond timer - COMPLETED" << endl;
467
468 ndbout << "= TEST10 ===============================" << endl;
469
470 testmutex = NdbMutex_Create();
471 testcond = NdbCondition_Create();
472 testthreadsdone = 0;
473
474 for (int i = 0; i < T3_THREADS; i++)
475 {
476 t3args[i] = i;
477 t3threads[i] = NdbThread_Create(testTryLockfunc, // Function
478 (void**)&t3args[i],// Arg
479 2048, // Stacksize
480 (char*)"test10thread", // Thread name
481 NDB_THREAD_PRIO_MEAN); // Thread priority
482 }
483
484 ndbout << "All threads created" << endl;
485
486 if (NdbMutex_Lock(testmutex) != 0)
487 fail("TEST10", "NdbMutex_Lock failed");
488
489 while (testthreadsdone < T3_THREADS*10)
490 {
491 if(NdbCondition_Wait(testcond, testmutex) != 0)
492 fail("TEST10", "NdbCondition_WaitTimeout failed");
493 ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
494 }
495 if (NdbMutex_Unlock(testmutex) != 0)
496 fail("TEST10", "NdbMutex_Unlock failed");
497
498 for (int i = 0; i < T3_THREADS; i++)
499 {
500 if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
501 fail("TEST10", "NdbThread_WaitFor failed");
502
503 NdbThread_Destroy(&t3threads[i]);
504 }
505
506 NdbMutex_Destroy(testmutex);
507 NdbCondition_Destroy(testcond);
508
509 ndbout << "TEST10 completed" << endl;
510
511
512 // Check total status of test
513
514 if (TestHasFailed == 1)
515 ndbout << endl << "TEST FAILED!" << endl;
516 else
517 ndbout << endl << "TEST PASSED!" << endl;
518
519 return TestHasFailed;
520
521 };
522
time_diff(Uint64 s1,Uint64 s2,Uint32 m1,Uint32 m2)523 Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2){
524
525 Uint64 diff = 0;
526 diff += (s2 - s1) * 1000000;
527 if(m2 >= m1)
528 diff += (m2 - m1);
529 else {
530 diff += m2;
531 diff -= m1;
532 }
533
534 // if(0)
535 // ndbout("(s1,m1) = (%d, %d) (s2,m2) = (%d, %d) -> diff = %d\n",
536 // (Uint32)s1,m1,(Uint32)s2,m2, (Uint32)diff);
537
538 return diff;
539 };
540
541 void
testMicros(int count)542 testMicros(int count){
543 Uint32 avg = 0;
544 Uint32 sum2 = 0;
545
546 for(int i = 0; i<count; i++){
547
548 const NDB_TICKS t1 = NdbTick_getCurrentTicks();
549
550 Uint32 r = (rand() % 1000) + 1;
551 NdbSleep_MilliSleep(r);
552
553 const NDB_TICKS t2 = NdbTick_getCurrentTicks();
554 const Uint64 m = NdbTick_Elapsed(t1,t2).microSec();
555 if(verbose)
556 ndbout << "Slept for " << r << " ms"
557 << " - Measured " << m << " us" << endl;
558
559 if(m > (r*1000)){
560 avg += (m - (r*1000));
561 sum2 += (m - (r*1000)) * (m - (r*1000));
562 } else {
563 avg += ((r*1000) - m);
564 sum2 += ((r*1000) - m) * ((r*1000) - m);
565 }
566 #if 0
567 m /= 1000;
568 if(m > r && ((m - r) > 10)){
569 ndbout << "Difference to big: " << (m - r) << " - Test failed" << endl;
570 TestHasFailed = 1;
571 }
572 if(m < r && ((r - m) > 10)){
573 ndbout << "Difference to big: " << (r - m) << " - Test failed" << endl;
574 TestHasFailed = 1;
575 }
576 #endif
577 }
578
579 Uint32 dev = (avg * avg - sum2) / count; dev /= count;
580 avg /= count;
581
582 Uint32 t = 0;
583 while((t*t)<dev) t++;
584 ndbout << "NOTE - measure are compared to NdbSleep_MilliSleep(...)" << endl;
585 ndbout << "Average error = " << avg << " us" << endl;
586 ndbout << "Stddev error = " << t << " us" << endl;
587 }
588