]> pd.if.org Git - liblfds/blob - liblfds/liblfds6.1.1/test/src/test_freelist.c
Initial import (all versions, including the new 7.1.0)
[liblfds] / liblfds / liblfds6.1.1 / test / src / test_freelist.c
1 #include "internal.h"\r
2 \r
3 \r
4 \r
5 \r
6 \r
7 /****************************************************************************/\r
8 void test_lfds611_freelist( void )\r
9 {\r
10   printf( "\n"\r
11           "Freelist Tests\n"\r
12           "==============\n" );\r
13 \r
14   freelist_test_internal_popping();\r
15   freelist_test_internal_pushing();\r
16   freelist_test_internal_popping_and_pushing();\r
17   freelist_test_internal_rapid_popping_and_pushing();\r
18 \r
19   return;\r
20 }\r
21 \r
22 \r
23 \r
24 \r
25 \r
26 /****************************************************************************/\r
27 void freelist_test_internal_popping( void )\r
28 {\r
29   unsigned int\r
30     loop,\r
31     cpu_count;\r
32 \r
33   lfds611_atom_t\r
34     count = 0;\r
35 \r
36   thread_state_t\r
37     *thread_handles;\r
38 \r
39   enum lfds611_data_structure_validity\r
40     dvs = LFDS611_VALIDITY_VALID;\r
41 \r
42   struct lfds611_freelist_state\r
43     *fs;\r
44 \r
45   struct lfds611_freelist_element\r
46     *fe;\r
47 \r
48   struct freelist_test_popping_state\r
49     *ftps;\r
50 \r
51   unsigned int\r
52     *found_count;\r
53 \r
54   /* TRD : we create a freelist with 1,000,000 elements\r
55 \r
56            the creation function runs in a single thread and creates\r
57            and pushes those elements onto the freelist\r
58 \r
59            each element contains a void pointer which is its element number\r
60 \r
61            we then run one thread per CPU\r
62            where each thread loops, popping as quickly as possible\r
63            each popped element is pushed onto a thread-local freelist\r
64 \r
65            the threads run till the source freelist is empty\r
66 \r
67            we then check the thread-local freelists\r
68            we should find we have every element\r
69 \r
70            then tidy up\r
71   */\r
72 \r
73   internal_display_test_name( "Popping" );\r
74 \r
75   cpu_count = abstraction_cpu_count();\r
76 \r
77   lfds611_freelist_new( &fs, 1000000, freelist_test_internal_popping_init, &count );\r
78   ftps = malloc( sizeof(struct freelist_test_popping_state) * cpu_count );\r
79   for( loop = 0 ; loop < cpu_count ; loop++ )\r
80   {\r
81     (ftps+loop)->fs = fs;\r
82     lfds611_freelist_new( &(ftps+loop)->fs_thread_local, 0, NULL, NULL );\r
83   }\r
84 \r
85   thread_handles = malloc( sizeof(thread_state_t) * cpu_count );\r
86 \r
87   for( loop = 0 ; loop < cpu_count ; loop++ )\r
88     abstraction_thread_start( &thread_handles[loop], loop, freelist_test_internal_thread_popping, ftps+loop );\r
89 \r
90   for( loop = 0 ; loop < cpu_count ; loop++ )\r
91     abstraction_thread_wait( thread_handles[loop] );\r
92 \r
93   free( thread_handles );\r
94 \r
95   // TRD : now we check the thread-local freelists\r
96   found_count = malloc( sizeof(unsigned int) * 1000000 );\r
97   for( loop = 0 ; loop < 1000000 ; loop++ )\r
98     *(found_count+loop) = 0;\r
99 \r
100   for( loop = 0 ; loop < cpu_count ; loop++ )\r
101   {\r
102     while( lfds611_freelist_pop((ftps+loop)->fs_thread_local, &fe) )\r
103     {\r
104       lfds611_freelist_get_user_data_from_element( fe, (void **) &count );\r
105       (*(found_count+count))++;\r
106       lfds611_freelist_push( fs, fe );\r
107     }\r
108   }\r
109 \r
110   for( loop = 0 ; loop < 1000000 and dvs == LFDS611_VALIDITY_VALID ; loop++ )\r
111   {\r
112     if( *(found_count+loop) == 0 )\r
113       dvs = LFDS611_VALIDITY_INVALID_MISSING_ELEMENTS;\r
114 \r
115     if( *(found_count+loop) > 1 )\r
116       dvs = LFDS611_VALIDITY_INVALID_ADDITIONAL_ELEMENTS;\r
117   }\r
118 \r
119   // TRD : cleanup\r
120   free( found_count );\r
121   for( loop = 0 ; loop < cpu_count ; loop++ )\r
122     lfds611_freelist_delete( (ftps+loop)->fs_thread_local, NULL, NULL );\r
123   free( ftps );\r
124   lfds611_freelist_delete( fs, NULL, NULL );\r
125 \r
126   // TRD : print the test result\r
127   internal_display_test_result( 1, "freelist", dvs );\r
128 \r
129   return;\r
130 }\r
131 \r
132 \r
133 \r
134 \r
135 \r
136 /****************************************************************************/\r
137 #pragma warning( disable : 4100 )\r
138 \r
139 int freelist_test_internal_popping_init( void **user_data, void *user_state )\r
140 {\r
141   lfds611_atom_t\r
142     *count;\r
143 \r
144   assert( user_data != NULL );\r
145   assert( user_state != NULL );\r
146 \r
147   count = (lfds611_atom_t *) user_state;\r
148 \r
149   *(lfds611_atom_t *) user_data = (*count)++;\r
150 \r
151   return( 1 );\r
152 }\r
153 \r
154 #pragma warning( default : 4100 )\r
155 \r
156 \r
157 \r
158 \r
159 \r
160 /****************************************************************************/\r
161 thread_return_t CALLING_CONVENTION freelist_test_internal_thread_popping( void *freelist_test_popping_state )\r
162 {\r
163   struct freelist_test_popping_state\r
164     *ftps;\r
165 \r
166   struct lfds611_freelist_element\r
167     *fe;\r
168 \r
169   assert( freelist_test_popping_state != NULL );\r
170 \r
171   ftps = (struct freelist_test_popping_state *) freelist_test_popping_state;\r
172 \r
173   lfds611_freelist_use( ftps->fs );\r
174   lfds611_freelist_use( ftps->fs_thread_local );\r
175 \r
176   while( lfds611_freelist_pop(ftps->fs, &fe) )\r
177     lfds611_freelist_push( ftps->fs_thread_local, fe );\r
178 \r
179   return( (thread_return_t) EXIT_SUCCESS );\r
180 }\r
181 \r
182 \r
183 \r
184 \r
185 \r
186 /****************************************************************************/\r
187 void freelist_test_internal_pushing( void )\r
188 {\r
189   unsigned int\r
190     loop,\r
191     cpu_count;\r
192 \r
193   lfds611_atom_t\r
194     count = 0;\r
195 \r
196   thread_state_t\r
197     *thread_handles;\r
198 \r
199   enum lfds611_data_structure_validity\r
200     dvs;\r
201 \r
202   struct freelist_test_pushing_state\r
203     *ftps;\r
204 \r
205   struct lfds611_freelist_element\r
206     *fe;\r
207 \r
208   struct lfds611_freelist_state\r
209     *fs,\r
210     *cleanup_fs;\r
211 \r
212   struct freelist_test_counter_and_thread_number\r
213     *cnt,\r
214     *counter_and_number_trackers;\r
215 \r
216   struct lfds611_validation_info\r
217     vi;\r
218 \r
219   /* TRD : we create an empty freelist, which we will push to\r
220 \r
221            we then create one freelist per CPU, where this freelist\r
222            contains 100,000 elements per thread and\r
223            each element is an incrementing counter and unique ID\r
224            (from 0 to number of CPUs)\r
225 \r
226            we then start one thread per CPU, where each thread is\r
227            given one of the populated freelists and pops from that\r
228            to push to the empty freelist\r
229 \r
230            the reason for this is to achieve memory pre-allocation\r
231            which allows the pushing threads to run at maximum speed\r
232 \r
233            the threads end when their freelists are empty\r
234 \r
235            we then fully pop the now populated main freelist (onto\r
236            a second freelist, so we can cleanly free all memory),\r
237            checking that the counts increment on a per unique ID basis\r
238            and that the number of elements we pop equals 1,000,000 per thread\r
239            (since each element has an incrementing counter which is\r
240             unique on a per unique ID basis, we can know we didn't lose\r
241             any elements)\r
242   */\r
243 \r
244   internal_display_test_name( "Pushing" );\r
245 \r
246   cpu_count = abstraction_cpu_count();\r
247 \r
248   ftps = malloc( sizeof(struct freelist_test_pushing_state) * cpu_count );\r
249 \r
250   lfds611_freelist_new( &fs, 0, NULL, NULL );\r
251 \r
252   for( loop = 0 ; loop < cpu_count ; loop++ )\r
253   {\r
254     (ftps+loop)->thread_number = (lfds611_atom_t) loop;\r
255     // TRD : note count is shared across threads, so thread 0 is 0-100000, thread 1 is 100000-200000, etc\r
256     (ftps+loop)->count = &count;\r
257     lfds611_freelist_new( &(ftps+loop)->source_fs, 100000, freelist_test_internal_pushing_init, (void *) (ftps+loop) );\r
258     (ftps+loop)->fs = fs;\r
259   }\r
260 \r
261   thread_handles = malloc( sizeof(thread_state_t) * cpu_count );\r
262 \r
263   for( loop = 0 ; loop < cpu_count ; loop++ )\r
264     abstraction_thread_start( &thread_handles[loop], loop, freelist_test_internal_thread_pushing, ftps+loop );\r
265 \r
266   for( loop = 0 ; loop < cpu_count ; loop++ )\r
267     abstraction_thread_wait( thread_handles[loop] );\r
268 \r
269   free( thread_handles );\r
270 \r
271   // TRD : now fully pop and verify the main freelist\r
272   lfds611_freelist_new( &cleanup_fs, 0, NULL, NULL );\r
273 \r
274   counter_and_number_trackers = malloc( sizeof(struct freelist_test_counter_and_thread_number) * cpu_count );\r
275 \r
276   for( loop = 0 ; loop < cpu_count ; loop++ )\r
277   {\r
278     (counter_and_number_trackers+loop)->counter = 100000 * loop;\r
279     (counter_and_number_trackers+loop)->thread_number = (lfds611_atom_t) loop;\r
280   }\r
281 \r
282   vi.min_elements = vi.max_elements = 100000 * cpu_count;\r
283 \r
284   lfds611_freelist_query( fs, LFDS611_FREELIST_QUERY_VALIDATE, &vi, (void *) &dvs );\r
285 \r
286   while( dvs == LFDS611_VALIDITY_VALID and lfds611_freelist_pop(fs, &fe) )\r
287   {\r
288     lfds611_freelist_get_user_data_from_element( fe, (void **) &cnt );\r
289 \r
290     if( cnt->counter != (counter_and_number_trackers+cnt->thread_number)->counter++ )\r
291       dvs = LFDS611_VALIDITY_INVALID_MISSING_ELEMENTS;\r
292 \r
293     lfds611_freelist_push( cleanup_fs, fe );\r
294   }\r
295 \r
296   // TRD : clean up\r
297   free( counter_and_number_trackers );\r
298 \r
299   for( loop = 0 ; loop < cpu_count ; loop++ )\r
300     lfds611_freelist_delete( (ftps+loop)->source_fs, NULL, NULL );\r
301 \r
302   free( ftps );\r
303 \r
304   lfds611_freelist_delete( cleanup_fs, freelist_test_internal_pushing_delete, NULL );\r
305   lfds611_freelist_delete( fs, NULL, NULL );\r
306 \r
307   // TRD : print the test result\r
308   internal_display_test_result( 1, "freelist", dvs );\r
309 \r
310   return;\r
311 }\r
312 \r
313 \r
314 \r
315 \r
316 \r
317 /****************************************************************************/\r
318 int freelist_test_internal_pushing_init( void **user_data, void *user_state )\r
319 {\r
320   struct freelist_test_counter_and_thread_number\r
321     *ftcatn;\r
322 \r
323   struct freelist_test_pushing_state\r
324     *ftps;\r
325 \r
326   assert( user_data != NULL );\r
327   // TRD : user_state is being used as an integer type\r
328 \r
329   *user_data = malloc( sizeof(struct freelist_test_counter_and_thread_number) );\r
330   ftps = (struct freelist_test_pushing_state *) user_state;\r
331 \r
332   ftcatn = (struct freelist_test_counter_and_thread_number *) *user_data;\r
333 \r
334   ftcatn->counter = (*ftps->count)++;\r
335   ftcatn->thread_number = ftps->thread_number;\r
336 \r
337   return( 1 );\r
338 }\r
339 \r
340 \r
341 \r
342 \r
343 \r
344 /****************************************************************************/\r
345 #pragma warning( disable : 4100 )\r
346 \r
347 void freelist_test_internal_pushing_delete( void *user_data, void *user_state )\r
348 {\r
349   assert( user_data != NULL );\r
350   assert( user_state == NULL );\r
351 \r
352   free( user_data );\r
353 \r
354   return;\r
355 }\r
356 \r
357 #pragma warning( default : 4100 )\r
358 \r
359 \r
360 \r
361 \r
362 \r
363 /****************************************************************************/\r
364 thread_return_t CALLING_CONVENTION freelist_test_internal_thread_pushing( void *freelist_test_pushing_state )\r
365 {\r
366   struct freelist_test_pushing_state\r
367     *ftps;\r
368 \r
369   struct lfds611_freelist_element\r
370     *fe;\r
371 \r
372   assert( freelist_test_pushing_state != NULL );\r
373 \r
374   ftps = (struct freelist_test_pushing_state *) freelist_test_pushing_state;\r
375 \r
376   lfds611_freelist_use( ftps->source_fs );\r
377   lfds611_freelist_use( ftps->fs );\r
378 \r
379   while( lfds611_freelist_pop(ftps->source_fs, &fe) )\r
380     lfds611_freelist_push( ftps->fs, fe );\r
381 \r
382   return( (thread_return_t) EXIT_SUCCESS );\r
383 }\r
384 \r
385 \r
386 \r
387 \r
388 \r
389 /****************************************************************************/\r
390 void freelist_test_internal_popping_and_pushing( void )\r
391 {\r
392   unsigned int\r
393     loop,\r
394     cpu_count;\r
395 \r
396   thread_state_t\r
397     *thread_handles;\r
398 \r
399   enum lfds611_data_structure_validity\r
400     dvs;\r
401 \r
402   struct lfds611_freelist_state\r
403     *fs;\r
404 \r
405   struct freelist_test_popping_and_pushing_state\r
406     *pps;\r
407 \r
408   struct lfds611_validation_info\r
409     vi;\r
410 \r
411   /* TRD : we have two threads per CPU\r
412            the threads loop for ten seconds\r
413            the first thread pushes 100000 elements then pops 100000 elements\r
414            the second thread pops 100000 elements then pushes 100000 elements\r
415            all pushes and pops go onto the single main freelist\r
416 \r
417            after time is up, all threads push what they have remaining onto\r
418            the main freelist\r
419 \r
420            we then validate the main freelist\r
421   */\r
422 \r
423   internal_display_test_name( "Popping and pushing (10 seconds)" );\r
424 \r
425   cpu_count = abstraction_cpu_count();\r
426 \r
427   lfds611_freelist_new( &fs, 100000 * cpu_count, NULL, NULL );\r
428 \r
429   pps = malloc( sizeof(struct freelist_test_popping_and_pushing_state) * cpu_count * 2 );\r
430 \r
431   for( loop = 0 ; loop < cpu_count ; loop++ )\r
432   {\r
433     (pps+loop)->fs = fs;\r
434     lfds611_freelist_new( &(pps+loop)->local_fs, 0, NULL, NULL );\r
435 \r
436     (pps+loop+cpu_count)->fs = fs;\r
437     lfds611_freelist_new( &(pps+loop+cpu_count)->local_fs, 100000, NULL, NULL );\r
438   }\r
439 \r
440   thread_handles = malloc( sizeof(thread_state_t) * cpu_count * 2 );\r
441 \r
442   for( loop = 0 ; loop < cpu_count ; loop++ )\r
443   {\r
444     abstraction_thread_start( &thread_handles[loop], loop, freelist_test_internal_thread_popping_and_pushing_start_popping, pps+loop );\r
445     abstraction_thread_start( &thread_handles[loop+cpu_count], loop, freelist_test_internal_thread_popping_and_pushing_start_pushing, pps+loop+cpu_count );\r
446   }\r
447 \r
448   for( loop = 0 ; loop < cpu_count * 2 ; loop++ )\r
449     abstraction_thread_wait( thread_handles[loop] );\r
450 \r
451   free( thread_handles );\r
452 \r
453   for( loop = 0 ; loop < cpu_count * 2 ; loop++ )\r
454     lfds611_freelist_delete( (pps+loop)->local_fs, NULL, NULL );\r
455 \r
456   free( pps );\r
457 \r
458   vi.min_elements = vi.max_elements = 100000 * cpu_count * 2;\r
459 \r
460   lfds611_freelist_query( fs, LFDS611_FREELIST_QUERY_VALIDATE, (void *) &vi, (void *) &dvs );\r
461 \r
462   lfds611_freelist_delete( fs, NULL, NULL );\r
463 \r
464   // TRD : print the test result\r
465   internal_display_test_result( 1, "freelist", dvs );\r
466 \r
467   return;\r
468 }\r
469 \r
470 \r
471 \r
472 \r
473 \r
474 \r
475 /****************************************************************************/\r
476 thread_return_t CALLING_CONVENTION freelist_test_internal_thread_popping_and_pushing_start_popping( void *freelist_test_popping_and_pushing_state )\r
477 {\r
478   struct freelist_test_popping_and_pushing_state\r
479     *pps;\r
480 \r
481   struct lfds611_freelist_element\r
482     *fe;\r
483 \r
484   time_t\r
485     start_time;\r
486 \r
487   unsigned int\r
488     count;\r
489 \r
490   assert( freelist_test_popping_and_pushing_state != NULL );\r
491 \r
492   pps = (struct freelist_test_popping_and_pushing_state *) freelist_test_popping_and_pushing_state;\r
493 \r
494   lfds611_freelist_use( pps->fs );\r
495   lfds611_freelist_use( pps->local_fs );\r
496 \r
497   time( &start_time );\r
498 \r
499   while( time(NULL) < start_time + 10 )\r
500   {\r
501     count = 0;\r
502 \r
503     while( count < 100000 )\r
504     {\r
505       lfds611_freelist_pop( pps->fs, &fe );\r
506 \r
507       if( fe != NULL )\r
508       {\r
509         lfds611_freelist_push( pps->local_fs, fe );\r
510         count++;\r
511       }\r
512     }\r
513 \r
514     while( lfds611_freelist_pop(pps->local_fs, &fe) )\r
515       lfds611_freelist_push( pps->fs, fe );\r
516   }\r
517 \r
518   return( (thread_return_t) EXIT_SUCCESS );\r
519 }\r
520 \r
521 \r
522 \r
523 \r
524 \r
525 /****************************************************************************/\r
526 thread_return_t CALLING_CONVENTION freelist_test_internal_thread_popping_and_pushing_start_pushing( void *freelist_test_popping_and_pushing_state )\r
527 {\r
528   struct freelist_test_popping_and_pushing_state\r
529     *pps;\r
530 \r
531   struct lfds611_freelist_element\r
532     *fe;\r
533 \r
534   time_t\r
535     start_time;\r
536 \r
537   unsigned int\r
538     count;\r
539 \r
540   assert( freelist_test_popping_and_pushing_state != NULL );\r
541 \r
542   pps = (struct freelist_test_popping_and_pushing_state *) freelist_test_popping_and_pushing_state;\r
543 \r
544   lfds611_freelist_use( pps->fs );\r
545   lfds611_freelist_use( pps->local_fs );\r
546 \r
547   time( &start_time );\r
548 \r
549   while( time(NULL) < start_time + 10 )\r
550   {\r
551     while( lfds611_freelist_pop(pps->local_fs, &fe) )\r
552       lfds611_freelist_push( pps->fs, fe );\r
553 \r
554     count = 0;\r
555 \r
556     while( count < 1000 )\r
557     {\r
558       lfds611_freelist_pop( pps->fs, &fe );\r
559 \r
560       if( fe != NULL )\r
561       {\r
562         lfds611_freelist_push( pps->local_fs, fe );\r
563         count++;\r
564       }\r
565     }\r
566   }\r
567 \r
568   // TRD : now push whatever we have in our local freelist\r
569   while( lfds611_freelist_pop(pps->local_fs, &fe) )\r
570     lfds611_freelist_push( pps->fs, fe );\r
571 \r
572   return( (thread_return_t) EXIT_SUCCESS );\r
573 }\r
574 \r
575 \r
576 \r
577 \r
578 \r
579 /****************************************************************************/\r
580 void freelist_test_internal_rapid_popping_and_pushing( void )\r
581 {\r
582   unsigned int\r
583     loop,\r
584     cpu_count;\r
585 \r
586   thread_state_t\r
587     *thread_handles;\r
588 \r
589   struct lfds611_freelist_state\r
590     *fs;\r
591 \r
592   struct lfds611_validation_info\r
593     vi;\r
594 \r
595   enum lfds611_data_structure_validity\r
596     dvs;\r
597 \r
598   /* TRD : in these tests there is a fundamental antagonism between\r
599            how much checking/memory clean up that we do and the\r
600            likelyhood of collisions between threads in their lock-free\r
601            operations\r
602 \r
603            the lock-free operations are very quick; if we do anything\r
604            much at all between operations, we greatly reduce the chance\r
605            of threads colliding\r
606 \r
607            so we have some tests which do enough checking/clean up that\r
608            they can tell the freelist is valid and don't leak memory\r
609            and here, this test now is one of those which does minimal\r
610            checking - in fact, the nature of the test is that you can't\r
611            do any real checking - but goes very quickly\r
612 \r
613            what we do is create a small freelist and then run one thread\r
614            per CPU, where each thread simply pops and then immediately\r
615            pushes\r
616 \r
617            the test runs for ten seconds\r
618 \r
619            after the test is done, the only check we do is to traverse\r
620            the freelist, checking for loops and ensuring the number of\r
621            elements is correct\r
622   */\r
623 \r
624   internal_display_test_name( "Rapid popping and pushing (10 seconds)" );\r
625 \r
626   cpu_count = abstraction_cpu_count();\r
627 \r
628   lfds611_freelist_new( &fs, cpu_count, NULL, NULL );\r
629 \r
630   thread_handles = malloc( sizeof(thread_state_t) * cpu_count );\r
631 \r
632   for( loop = 0 ; loop < cpu_count ; loop++ )\r
633     abstraction_thread_start( &thread_handles[loop], loop, freelist_test_internal_thread_rapid_popping_and_pushing, fs );\r
634 \r
635   for( loop = 0 ; loop < cpu_count ; loop++ )\r
636     abstraction_thread_wait( thread_handles[loop] );\r
637 \r
638   free( thread_handles );\r
639 \r
640   vi.min_elements = cpu_count;\r
641   vi.max_elements = cpu_count;\r
642 \r
643   lfds611_freelist_query( fs, LFDS611_FREELIST_QUERY_VALIDATE, (void *) &vi, (void *) &dvs );\r
644 \r
645   lfds611_freelist_delete( fs, NULL, NULL );\r
646 \r
647   // TRD : print the test result\r
648   internal_display_test_result( 1, "freelist", dvs );\r
649 \r
650   return;\r
651 }\r
652 \r
653 \r
654 \r
655 \r
656 \r
657 /****************************************************************************/\r
658 thread_return_t CALLING_CONVENTION freelist_test_internal_thread_rapid_popping_and_pushing( void *lfds611_freelist_state )\r
659 {\r
660   struct lfds611_freelist_state\r
661     *fs;\r
662 \r
663   struct lfds611_freelist_element\r
664     *fe;\r
665 \r
666   time_t\r
667     start_time;\r
668 \r
669   assert( lfds611_freelist_state != NULL );\r
670 \r
671   fs = (struct lfds611_freelist_state *) lfds611_freelist_state;\r
672 \r
673   lfds611_freelist_use( fs );\r
674 \r
675   time( &start_time );\r
676 \r
677   while( time(NULL) < start_time + 10 )\r
678   {\r
679     lfds611_freelist_pop( fs, &fe );\r
680     lfds611_freelist_push( fs, fe );\r
681   }\r
682 \r
683   return( (thread_return_t) EXIT_SUCCESS );\r
684 }\r
685 \r