1 /*
2  * copyright 2010-2012 Edscott Wilson Garcia (GPL-license)
3  *
4  * This is very simple example program to test 64 bit
5  * functions of the Disk Based Hash (DBH) and
6  * verify correct handling of dbh files greater than
7  * 2 Gb in size (up to 256^8/2).
8 
9  * A dbh file is created from a specified filesystem.
10  * Paths are indexed with g_string hash key
11  * Hash key collisions are noted in dbh file COLLISIONS
12  * Hash key<->path associations are noted in dbh file KEY*
13  * Hash key<->file are noted in dbh file INDEX
14  *
15  * usage: ./filesystem path option
16  * Option can be:
17  *    "index" (create KEY, COLLISIONS and INDEX dbh files)
18  *    "dump"  (do a foreach on all records and print summary)
19  *    "regen" (recreate INDEX dbh file with optimized fisical structure)
20  *    "compare" (compare each file in INDEX with actual file on disk)
21  *    "parallel"
22  *    "thread"
23  *    "fulltest" (all of the above)
24 
25  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 
30 #ifdef HAVE_LSTAT
31 # define LSTAT lstat
32 #else
33 # define LSTAT stat
34 #endif
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <dbh.h>
39 #include <dirent.h>
40 #include <sys/types.h>
41 #include <inttypes.h>
42 
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h>
45 #endif
46 
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <errno.h>
51 
52 #include <glib.h>
53 
54 #define DIRECTORY "testfiles"
55 #define KEY "testfiles/filesystem.key.dbh"
56 #define INDEX "testfiles/filesystem.index.dbh"
57 #define REBUILT "testfiles/filesystem.index.rebuilt.dbh"
58 #define TEST_INDEX "testfiles/parfilesystem.index.dbh"
59 #define COLLISIONS "testfiles/filesystem.collisions.dbh"
60 
61 #define HELP \
62 "     Options:\n"\
63 "       index: Create an index of items within the specified \"path\"\n"\
64 "        dump: Recalculate size of data items in DBH table (sweep/fanout)\n"\
65 "       regen: Regenerate the DBH table (sweep/fanout)\n"\
66 "      thread: Test concurrent light weight process activity\n"\
67 "    parallel: Test concurrent heavy weight process activity\n"\
68 "     compare: Compare data in DBH table to actual data\n"\
69 "    fulltest: All tests\n"\
70 " *To test a DBH table larger than 4 GB, choose a \"path\" with more than 4GB.\n"\
71 "  Do not alter any item within \"path\" during the test or error will occur."
72 
73 
74 typedef struct dump_t{
75     char **argv;
76     int original_count;
77     long long original_sum;
78     long long sum;
79     int which;
80     int count;
81 }dump_t;
82 
83 
84 static DBHashTable *dbh_key;
85 static
get_hash_key(unsigned char bucket,const char * pre_key)86 gchar *get_hash_key(unsigned char bucket, const char *pre_key){
87     GString *gs = g_string_new(pre_key);
88     gchar *key;
89     key=g_strdup_printf("%c%10u", bucket, g_string_hash(gs));
90     g_string_free(gs, TRUE);
91     return key;
92 }
93 
94 static int
read_filesystem(DBHashTable * dbh,const char * path,dump_t * dump_p)95 read_filesystem(DBHashTable *dbh, const char *path, dump_t *dump_p)
96 {
97     DIR *directory;
98     int count = 0;
99     struct dirent *d;
100 
101     directory = opendir(path);
102     if(!directory) {
103 	fprintf(stderr,"Cannot open %s\n" ,path);
104 	return -1;
105     }
106 #define     _BSD_SOURCE 1
107 while((d = readdir(directory)) != NULL)
108     {
109 
110         char *fullpath=NULL;
111 	gchar  *key;
112 	gboolean is_dir=FALSE;
113 	unsigned char bucket='A';
114         if(strcmp(d->d_name, ".")==0)  continue;
115         if(strcmp(d->d_name, "..")==0)  continue;
116 
117 
118 
119 
120         fullpath=g_build_filename(path,d->d_name,NULL);
121 
122 
123 	do {
124 	    key=get_hash_key(bucket,fullpath);
125             dbh_set_key (dbh,(unsigned char *)key);
126 	    bucket++;
127 	}
128 	while(dbh_load(dbh));
129 	bucket--;
130 	if (bucket > 'A'){
131 	    printf("HASH colision: %s -> %s\n", key,fullpath);
132 	    DBHashTable *dbh_inverse=dbh_new(COLLISIONS, NULL, 0);
133 	    char inverse_key[255];
134 	    memset(inverse_key,0,255);
135 	    strncpy(inverse_key,fullpath, 254);
136 	    dbh_set_key (dbh_inverse,(unsigned char *)inverse_key);
137 	    dbh_set_size(dbh_inverse,DBH_KEYLENGTH(dbh));
138 	    dbh_set_data(dbh_inverse,(void *)DBH_KEY(dbh),DBH_KEYLENGTH(dbh));
139 	    dbh_update(dbh_inverse);
140 	    dbh_close(dbh_inverse);
141 	}
142 
143 	dbh_set_key (dbh_key,(unsigned char *)key);
144 	dbh_set_size(dbh_key,strlen(fullpath)+1);
145 	dbh_set_data(dbh_key,(void *)fullpath,strlen(fullpath)+1);
146 	dbh_update(dbh_key);
147 	 struct stat st;
148 	 if (LSTAT(fullpath,&st)<0){
149 	    printf("cannot stat %s: %s\n",fullpath,strerror(errno));
150 	    continue;
151 	 }
152 
153 
154 	if (S_ISDIR(st.st_mode)) is_dir=TRUE;
155 
156         if (!is_dir) {
157 
158 	 if (st.st_size == 0) continue;
159          // Let's put a 100 MB limit for the test.
160 	 if (st.st_size > 100000000LL) {
161 	     printf("Skipping %s: file is too big, really (%lld)\n",fullpath, (long long)st.st_size);
162 	     continue;
163 	 }
164 	 if (!S_ISREG(st.st_mode)) {
165 	     continue;
166 	 }
167 	 // This is useful if our data size grows over 1024 B:
168 	 if (DBH_MAXIMUM_RECORD_SIZE(dbh) < st.st_size) {
169 	     dbh_set_size(dbh,st.st_size);
170 	     printf("dbh_set_size set to %lld\n",(long long)st.st_size);
171 	 }
172 	 int fd=open(fullpath,O_RDONLY);
173 	 if (fd < 0) {
174 	    printf("cannot open %s for read\n",fullpath);
175 	    continue;
176 	 }
177 	 // This works instead of dbh_set_data():
178 	 if (read(fd,DBH_DATA(dbh),st.st_size) < 0){
179 	    printf("problem reading %lld bytes from %s\n",
180 		    (long long)st.st_size,fullpath);
181 	    close(fd);
182 	    continue;
183 	 }
184 	 close(fd);
185 	 dbh_set_recordsize(dbh,st.st_size);
186 	 dbh_update(dbh);
187 	 dump_p->sum += st.st_size;
188 	 count++;
189          //fprintf(stderr,"%s\n",fullpath);
190 	 //
191 	}
192         if (is_dir) {
193 		int retval;
194 		retval = read_filesystem(dbh,fullpath, dump_p);
195 		if (retval > 0) count += retval;
196 	}
197 
198 	g_free(fullpath);
199     }
200     closedir(directory);
201 //	printf ("%s -> %d files\n",path,count);
202     return (count);
203 }
204 
operate(DBHashTable * dbh)205 static void  operate (DBHashTable *dbh){
206     dump_t *dump_p = dbh->user_data;
207     dump_p->count++;
208     //sum += strlen((char *)DBH_DATA(dbh));
209     dump_p->sum += DBH_RECORD_SIZE(dbh);
210 }
compare(DBHashTable * dbh)211 static void  compare (DBHashTable *dbh){
212     dbh_set_key (dbh_key,(unsigned char *)DBH_KEY(dbh));
213     dbh_load(dbh_key);
214     char *path=DBH_DATA(dbh_key);
215     int fd=open(path,O_RDONLY);
216 	 if (fd < 0) {
217 	    printf("cannot open %s for read\n",path);
218 	    return;
219 	 }
220 	 // This works instead of dbh_set_data():
221     struct stat st;
222     LSTAT(path,&st);
223     void *p=malloc(st.st_size);
224      if (p == NULL) {
225 	 fprintf(stderr, "malloc: %s\n", strerror(errno));
226 	exit(1);
227      }
228 	 if (read(fd,p,st.st_size) < 0){
229 	    printf("problem reading %lld bytes from %s\n",
230 		    (long long)st.st_size,path);
231 	    close(fd);
232 	    free(p);
233 	    return;
234 	 }
235 	 close(fd);
236     if (memcmp(p,DBH_DATA(dbh),st.st_size) != 0) {
237 	printf("%s does not compare!\n",path);
238     } else {
239 	static int count=0;
240 	if (count++ % 1000 == 0) {
241 	    printf ("."); fflush(stdout);
242 	}
243     }
244     free(p);
245 }
246 
247 static int
dump(dump_t * dump_p)248 dump(dump_t *dump_p) {
249     //char **argv, int which, int original_count, long long original_sum){
250     dump_p->count=0;   dump_p->sum=0;
251     const char *text;
252     if (dump_p->which) text = "Sweep"; else text = "Fanout";
253     fprintf(stdout,"%s is now being performed by pid %d\n", text, getpid());
254     // PARALLEL SAFE need not be specified on READ_ONLY
255     DBHashTable *dbh=dbh_new(INDEX, NULL, DBH_READ_ONLY);
256     dbh->user_data = dump_p;
257     if (dump_p->which) dbh_foreach_sweep (dbh,operate);
258     else dbh_foreach_fanout (dbh,operate);
259     dbh_close(dbh);
260     if (strcmp(dump_p->argv[2],"fulltest")==0) {
261 	if (dump_p->sum != dump_p->original_sum){
262 	  //g_warning("Original sum does not match %s sum (%I64d != %I64d)\nTest FAILED.\n",
263 	  g_warning("Original sum does not match %s sum (%lld != %lld)\nTest FAILED.\n",
264 		text, dump_p->original_sum, dump_p->sum);
265 	  exit(1);
266 	}
267 	if (dump_p->count != dump_p->original_count){
268 	    g_warning("Original count does not match %s count (%d != %d)\nTest FAILED.\n",
269 		text, dump_p->original_count, dump_p->count);
270 	  exit(1);
271 	}
272     }
273     fprintf(stdout,
274 "  Sweep data:\n"\
275 "    Items in the DBH table (filesystem count) = %d\n"\
276 "    Sum of data items size saved in DBH table = %lld\n",
277 	    dump_p->count, dump_p->sum);
278     if (strcmp(dump_p->argv[2],"fulltest")==0) {
279 	fprintf(stderr, "Test %s PASSED\n", text);
280     }
281     return 1;
282 }
283 
284 static void
check_files(void)285 check_files(void){
286     if (!g_file_test(INDEX, G_FILE_TEST_EXISTS)){
287       g_warning("Index file %s has not yet been created\n",
288 	      INDEX);
289       exit(1);
290     }
291     if (!g_file_test(COLLISIONS, G_FILE_TEST_EXISTS)){
292       g_warning("DBH table %s has not yet been created\n",
293 	      COLLISIONS);
294       exit(1);
295     }
296     if (!g_file_test(KEY, G_FILE_TEST_EXISTS)){
297       g_warning("DBH table %s has not yet been created\n",
298 	      KEY);
299       exit(1);
300     }
301 }
302 
303 DBHashTable *newdbh;
partest(DBHashTable * dbh)304 void  partest (DBHashTable *dbh){
305     // Assign key
306     memcpy(newdbh->key, dbh->key, DBH_KEYLENGTH(dbh));
307 
308     // Assign record size. This is not implicit with dbh_set_data, why not?
309     dbh_set_recordsize (newdbh, DBH_RECORD_SIZE(dbh));
310 
311     // Assign data
312     dbh_set_data (newdbh, dbh->data, DBH_RECORD_SIZE(dbh));
313     // Update record
314     dbh_update(newdbh);
315 }
316 
317 
318 static void *
parallel_test(void * data)319 parallel_test(void *data){
320     // sweep it.
321     // Open source dbh
322     DBHashTable *dbh;
323     dbh=dbh_new(INDEX, NULL, DBH_PARALLEL_SAFE);
324     if (!dbh) return NULL;
325     newdbh=dbh_new(TEST_INDEX, NULL, DBH_PARALLEL_SAFE);
326     dbh_foreach_sweep (dbh, partest);
327     //dbh_foreach_sweep (dbh,operate);
328     dbh_close(dbh);
329     dbh_close(newdbh);
330     return NULL;
331 }
332 
rebuild(DBHashTable * dbh_thread)333 static void  rebuild (DBHashTable *dbh_thread){
334     DBHashTable *rebuilt_dbh = dbh_thread->user_data;
335     // Adquire mutex.
336     dbh_mutex_lock(rebuilt_dbh);
337 
338     // Copy key and data to rebuilt_dbh
339     dbh_set_key(rebuilt_dbh, DBH_KEY(dbh_thread));
340     dbh_set_recordsize (rebuilt_dbh, DBH_RECORD_SIZE(dbh_thread));
341     dbh_set_data(rebuilt_dbh, DBH_DATA(dbh_thread), DBH_RECORD_SIZE(dbh_thread));
342     // Write to rebuilt dbh
343     dbh_update(rebuilt_dbh);
344     // Release mutex
345     dbh_mutex_unlock(rebuilt_dbh);
346     return;
347 }
348 
349 static void *
thread_test_f(gpointer data)350 thread_test_f(gpointer data){
351     // Each thread has its own copy, read-only
352     // Since no thread will be writing, DBH_PARALLEL_SAFE is not necesary.
353     DBHashTable *dbh_thread=dbh_new(INDEX, NULL, DBH_READ_ONLY);
354     // If any lingering locks are left behind by a parallel safe
355     // SIGINT, cleanup:
356     dbh_clear_locks(dbh_thread);
357     // Assign open thread-safe dbh to user_data
358     dbh_thread->user_data = data;
359     // This gets a writelock, only when PARALLEL_SAFE is defined,
360     // which is not necessary here.
361     dbh_foreach_sweep (dbh_thread, rebuild);
362     dbh_close(dbh_thread);
363     return NULL;
364 }
365 
366 static void *
thread_dump_f(gpointer data)367 thread_dump_f(gpointer data){
368     dump_t *dump_p = data;
369     int i; for(i=1; i>=0; i--) {dump_p->which = i; dump(dump_p);}
370     g_free(dump_p);
371     return NULL;
372 }
373 
374 
375 
main(int argc,char ** argv)376 int main(int argc, char **argv){
377 
378     dump_t dump_v;
379     memset(&dump_v, 0, sizeof(dump_t));
380     if (g_mkdir_with_parents(DIRECTORY, 0770) < 0){
381 	if (!g_file_test(DIRECTORY, G_FILE_TEST_IS_DIR)){
382 	    g_warning("mkdir(%s): %s\n", DIRECTORY, strerror(errno));
383             sleep(5);
384 
385 	    exit(1);
386 	}
387     }
388     if (!g_file_test(DIRECTORY, G_FILE_TEST_IS_DIR)){
389 	g_warning("Failed test: g_file_test(%s, G_FILE_TEST_IS_DIR)\n",
390 		DIRECTORY );
391         sleep(5);
392 	exit(1);
393     }
394 
395   if (argc < 3) {
396    fprintf(stderr,"insufficient arguments (%d < 3), usage: %s (path) (option)\n%s\n",
397 	   argc, argv[0], HELP);
398    fprintf(stderr, "\n<return> to continue"); fflush(stderr);
399    char buffer[10];
400    fgets(buffer, 10, stdin);
401    exit(1);
402   }
403   dump_v.argv=argv;
404 
405   // This DBH uses more than one bucket in order to handle hashtable
406   // key collisions.
407   // This is done in a single thread, non parallel mode.
408   if (strcmp(argv[2],"index")==0 || strcmp(argv[2],"fulltest")==0) {
409     fprintf(stderr, "///////////////////  DBH generation //////////////////////////\n");
410     fprintf(stdout,"Creating index now, process 0x%x recursively reading %s\n", getpid(), argv[1]);
411     // This table is the bucket index file. The index file is also
412     // the data table.
413     unsigned char key_length = 11;
414 //    DBHashTable *dbh=dbh_create(INDEX, key_length);
415     DBHashTable *dbh=dbh_new(INDEX, &key_length, DBH_CREATE);
416 //    dbh_key=dbh_create(KEY, key_length);
417     dbh_key=dbh_new(KEY, &key_length, DBH_CREATE);
418     // This table handles collisions. If a path is indexed here (first 254
419     // bytes of the string), then the data element is the actual hash table
420     // key. This avoids a collision with a path that has already been indexed.
421     key_length = 254;
422 //    DBHashTable *dbh_inverse=dbh_create(COLLISIONS, key_length);
423     DBHashTable *dbh_inverse=dbh_new(COLLISIONS, &key_length, DBH_CREATE);
424     dbh_close(dbh_inverse);
425     // Read the filesystem data into the DBH table.
426     dump_v.sum = 0;
427     dump_v.original_count=read_filesystem(dbh,argv[1],&dump_v);
428     dump_v.original_sum = dump_v.sum;
429     dbh_close(dbh);
430     dbh_close(dbh_key);
431     fprintf(stdout,
432 "  Index created:\n"\
433 "    Items in the DBH table (filesystem count) = %d\n"\
434 "    Sum of data items size saved in DBH table = %lld\n",
435 	    dump_v.original_count, dump_v.original_sum);
436     if (strcmp(argv[2],"index")==0) exit(0);
437   }
438   // Full or specific test follows.
439   check_files();
440   // Dump test
441   if (strcmp(argv[2],"dump")==0 || strcmp(argv[2],"fulltest")==0) {
442       // Find out how many items and total size of data records
443       // a sweep/fanout of DBH table will find
444       int i; for(i=1; i>=0; i--) {dump_v.which = i; dump(&dump_v);}
445   }
446 
447   // Regen tests
448   if (strcmp(argv[2],"regen")==0 || strcmp(argv[2],"fulltest")==0) {
449     fprintf(stderr, "///////////////////  Serial tests //////////////////////////\n");
450     fprintf(stdout,"Performing regen_sweep now...\n");
451     DBHashTable *dbh;
452     dbh=dbh_new(INDEX, NULL, 0);
453     dbh_regen_sweep(&dbh);
454     dbh_close(dbh);
455     // Find out how many items and total size of data records
456     // a sweep of DBH table will find
457     dump_v.which = 1;
458     dump(&dump_v);
459     fprintf(stdout,"Performing regen_fanout now...\n");
460     dbh=dbh_new(INDEX, NULL, 0);
461     dbh_regen_fanout(&dbh);
462     dbh_close(dbh);
463     // Find out how many items and total size of data records
464     // a sweep of DBH table will find
465     dump_v.which = 0;
466     dump(&dump_v);
467   }
468 
469 
470   // Parallel test
471 #ifndef HAVE_FORK
472   if (strcmp(argv[2],"parallel_read")==0){
473           fprintf(stdout, "Parallel read test, pid: %d\n", getpid()); fflush(stdout);
474             int i; for(i=1; i>=0; i--){
475                     dump_v.which = i;
476                     dump(&dump_v);
477             }
478 	    return(0x0);
479   }
480   if (strcmp(argv[2],"parallel_write")==0){
481           fprintf(stdout, "Parallel write test, pid: %d\n", getpid()); fflush(stdout);
482 	    parallel_test(NULL);
483 	    return(0x0);
484   }
485 #endif
486   if (strcmp(argv[2],"parallel")==0 || strcmp(argv[2],"fulltest")==0){
487 
488 #define CHILDREN 5
489 #ifdef HAVE_FORK
490     pid_t pid[CHILDREN];
491 #else
492     int pid[CHILDREN];
493 #endif
494     int id;
495     // Read test...
496     fprintf(stderr, "///////////////////  Parallel read tests //////////////////////////\n");
497     fprintf(stdout,"Performing parallel read test on %d processes...\n", CHILDREN);
498     for (id=0; id<CHILDREN; id++){
499 #ifdef HAVE_FORK
500 	pid[id] = fork();
501 	if (!pid[id]) {
502 	    int i; for(i=1; i>=0; i--) {dump_v.which = i; dump(&dump_v);}
503 	    return(0x01);
504 	}
505 #else
506         const char *arg[] = {argv[0], argv[1], "parallel_read" ,NULL};
507 	pid[id] = _spawnvp(P_NOWAIT, arg[0], arg);
508 #endif
509 
510     }
511 
512     for (id=0; id<CHILDREN; id++){
513 	int status;
514 #ifdef HAVE_FORK
515 
516 	int p = wait(&status);
517 	fprintf(stderr,"read test for process 0x%x done... status=%s\n",
518 		(int) p, WIFEXITED(status) && WEXITSTATUS(status)?"PASSED":"FAILED");
519 #else
520         int ret = _cwait(&status, pid[id], 0);
521 	fprintf(stderr,"read test for process 0x%x done... (%s)\n",
522 			pid[id], (ret < 0)?strerror(errno):"exit OK");
523 #endif
524 
525     }
526 
527 #ifndef PARALLEL_SAFE
528     fprintf(stderr,"Skipping parallel write tests (not implemented on this platform)...\n");
529 #else
530     fprintf(stderr, "///////////////////  Parallel write tests //////////////////////////\n");
531     fprintf(stdout,"Performing parallel write test on %d processes...\n", CHILDREN);
532     // Write test
533     DBHashTable *dbh;
534     dbh=dbh_new(INDEX, NULL, 0);
535     unsigned char key_length = DBH_KEYLENGTH(dbh);
536     newdbh=dbh_new(TEST_INDEX, &key_length, DBH_CREATE);
537     if (DBH_MAXIMUM_RECORD_SIZE(dbh) > DBH_MAXIMUM_RECORD_SIZE(newdbh)){
538 	dbh_set_size (newdbh, DBH_MAXIMUM_RECORD_SIZE(dbh));
539     }
540     dbh_close(dbh);
541     dbh_close(newdbh);
542     for (id=0; id<CHILDREN; id++){
543 #ifdef HAVE_FORK
544 	pid[id] = fork();
545 	if (!pid[id]) {
546 	    parallel_test(NULL);
547 	    return 1;
548 	}
549 #else
550         const char *arg[] = {argv[0], argv[1], "parallel_write" ,NULL};
551 	pid[id] = _spawnvp(P_NOWAIT, arg[0], arg);
552 #endif
553     }
554     for (id=0; id<CHILDREN; id++){
555 	int status;
556 #ifdef HAVE_FORK
557 	pid_t p = wait(&status);
558 	fprintf(stderr,"write test for process 0x%x done... status=%s\n",
559 		(int) p, WIFEXITED(status) && WEXITSTATUS(status)?"PASSED":"FAILED");
560 #else
561         int ret = _cwait(&status, pid[id], 0);
562 	fprintf(stderr,"write test for process 0x%x done...  (%s)\n",
563 		pid[id],  (ret < 0)?strerror(errno):"exit OK");
564 #endif
565     }
566     fprintf(stdout,"Verifying integrity of parallel write test output...\n");
567     // Copy newly created index to index.
568 
569     if (rename(TEST_INDEX, INDEX) < 0){
570 	g_warning("rename (%s, %s): %s\n", TEST_INDEX, INDEX, strerror(errno));
571     }
572 
573     //dbh=dbh_open(INDEX);
574     //dbh_regen_sweep(&dbh);
575     //dbh_close(dbh);
576     // Find out how many items and total size of data records
577     // a sweep of DBH table will find
578     int i; for(i=1; i>=0; i--) {dump_v.which = i; dump(&dump_v);}
579 
580 #endif
581   }
582 
583 #if GLIB_MAJOR_VERSION==2 && GLIB_MINOR_VERSION<32
584     g_thread_init(NULL);
585 #endif
586 
587   // Thread test
588   if (strcmp(argv[2],"thread")==0 || strcmp(argv[2],"fulltest")==0){
589     fprintf(stderr, "///////////////////  Thread read test //////////////////////////\n");
590 #define THREADS 6
591     GThread *thread[THREADS];
592 
593     int id;
594     for (id=0; id<THREADS; id++){
595 	dump_t *dump_p = (dump_t *)malloc(sizeof(dump_t));
596      if (dump_p == NULL) {
597 	 fprintf(stderr, "malloc: %s\n", strerror(errno));
598 	exit(1);
599      }
600 	memcpy(dump_p, &dump_v, sizeof(dump_t));
601 #if GLIB_MAJOR_VERSION==2 && GLIB_MINOR_VERSION<32
602 	thread[id] = g_thread_create(thread_dump_f, dump_p, TRUE, NULL);
603 #else
604 	thread[id] = g_thread_try_new(NULL, thread_dump_f, dump_p, NULL);
605 #endif
606 	fprintf(stdout,"  thread 0x%x started\n", GPOINTER_TO_INT(thread[id]));
607 	fflush(stdout);
608     }
609     for (id=0; id<THREADS; id++){
610 	if (thread[id])	{
611 	    g_thread_join(thread[id]);
612 	    fprintf(stdout,"  thread 0x%x joined.\n", GPOINTER_TO_INT(thread[id]));
613 	    fflush(stdout);
614 	}
615     }
616 
617     fprintf(stderr, "///////////////////  Thread read/write test //////////////////////////\n");
618 
619 #if 10
620     // Create empty DBH table:
621     unsigned char k = 11;
622     // DBH_THREAD_SAFE sets up the dbh table mutex for thread safe operation
623     DBHashTable *rebuilt_dbh=dbh_new(REBUILT, &k, DBH_CREATE|DBH_THREAD_SAFE);
624 
625     for (id=0; id<THREADS; id++){
626 #if GLIB_MAJOR_VERSION==2 && GLIB_MINOR_VERSION<32
627 	thread[id] = g_thread_create(thread_test_f, rebuilt_dbh, TRUE, NULL);
628 #else
629 	thread[id] = g_thread_try_new(NULL, thread_test_f, rebuilt_dbh, NULL);
630 #endif
631 	fprintf(stdout,"  thread 0x%x started\n", GPOINTER_TO_INT(thread[id]));
632 	fflush(stdout);
633     }
634     for (id=0; id<THREADS; id++){
635 	if (thread[id])	{
636 	    g_thread_join(thread[id]);
637 	    fprintf(stdout,"  thread 0x%x joined.\n", GPOINTER_TO_INT(thread[id]));
638 	    fflush(stdout);
639 	}
640     }
641     dbh_close(rebuilt_dbh);
642     rename (REBUILT, INDEX);
643     int i; for(i=1; i>=0; i--) {dump_v.which = i; dump(&dump_v);}
644 #endif
645   }
646   fprintf(stderr, "///////////////////  Final comparison test //////////////////////////\n");
647   // Comparison test
648   if (strcmp(argv[2],"compare")==0 || strcmp(argv[2],"fulltest")==0) {
649     fprintf(stdout,"Performing comparison test now (sweep)...\n");
650     DBHashTable *dbh;
651     dbh=dbh_new(INDEX, NULL, DBH_READ_ONLY);
652     dbh_key=dbh_new(KEY, NULL, DBH_READ_ONLY);
653     dbh_foreach_sweep (dbh,compare);
654     dbh_close(dbh);
655     dbh_close(dbh_key);
656     fprintf(stdout,"\n");
657     fprintf(stderr, "Test PASSED\n");
658 
659     fprintf(stdout,"Performing comparison test now (fanout)...\n");
660     dbh=dbh_new(INDEX, NULL, DBH_READ_ONLY);
661     dbh_key=dbh_new(KEY, NULL, DBH_READ_ONLY);
662     dbh_foreach_fanout (dbh,compare);
663     dbh_close(dbh);
664     dbh_close(dbh_key);
665     fprintf(stdout,"\n");
666     fprintf(stderr, "Test PASSED\n");
667 
668   }
669   fprintf(stderr, "All tests passed.\n");
670   exit(0);
671 }
672