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