1 static char rcsid[] = "$Id: access.c 222390 2020-04-10 12:44:01Z twu $";
2 #ifdef HAVE_CONFIG_H
3 #include <config.h>
4 #endif
5
6 #include "access.h"
7 #include "list.h"
8 #include "intlist.h"
9
10 #include <stdio.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h> /* For strerror */
14 #include <errno.h>
15 #include <sys/stat.h> /* For fstat */
16
17 /* <unistd.h> and <sys/types.h> included in access.h */
18 #include <sys/mman.h> /* For mmap */
19
20 #define PROJECT_ID 42
21 #include <sys/ipc.h>
22 #include <sys/shm.h> /* For shmat and shmdt */
23 #include "semaphore.h"
24
25
26 #ifdef USE_MPI
27 #include <mpi.h>
28 #endif
29
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h> /* For open */
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h> /* For open and fstat */
35 #endif
36
37 #ifdef PAGESIZE_VIA_SYSCONF
38 #include <unistd.h>
39 #endif
40 #ifdef PAGESIZE_VIA_SYSCTL
41 #include <sys/sysctl.h>
42 #endif
43
44 #include "assert.h"
45 #include "mem.h"
46 #include "types.h"
47 #include "fopen.h"
48 #include "stopwatch.h"
49
50 #ifdef WORDS_BIGENDIAN
51 #include "bigendian.h"
52 #else
53 #include "littleendian.h"
54 #endif
55
56
57 #define MAX_KILL_ATTEMPTS 5 /* Limits on attempts to kill unattached memory segments */
58
59
60 #ifdef DEBUG
61 #define debug(x) x
62 #else
63 #define debug(x)
64 #endif
65
66 static bool preload_shared_memory_p = false;
67 static bool unload_shared_memory_p = false;
68
69 void
Access_setup(bool preload_shared_memory_p_in,bool unload_shared_memory_p_in)70 Access_setup (bool preload_shared_memory_p_in, bool unload_shared_memory_p_in) {
71 preload_shared_memory_p = preload_shared_memory_p_in;
72 unload_shared_memory_p = unload_shared_memory_p_in;
73 return;
74 }
75
76
77
78 bool
Access_file_exists_p(char * filename)79 Access_file_exists_p (char *filename) {
80 #ifdef HAVE_STRUCT_STAT64
81 /* struct stat64 is now deprecated */
82 struct stat sb;
83 #else
84 struct stat sb;
85 #endif
86
87 #ifdef HAVE_STAT64
88 /* stat64 is now deprecated */
89 if (stat(filename,&sb) == 0) {
90 return true;
91 } else {
92 return false;
93 }
94 #else
95 if (stat(filename,&sb) == 0) {
96 return true;
97 } else {
98 return false;
99 }
100 #endif
101 }
102
103
104 size_t
Access_filesize(char * filename)105 Access_filesize (char *filename) {
106 #ifdef HAVE_STRUCT_STAT64
107 /* struct stat64 is now deprecated */
108 struct stat sb;
109 #else
110 struct stat sb;
111 #endif
112
113 #ifdef HAVE_STAT64
114 /* stat64 is now deprecated */
115 stat(filename,&sb);
116 #else
117 stat(filename,&sb);
118 #endif
119 debug(printf("filesize is %zu\n",(size_t) sb.st_size));
120 return (size_t) sb.st_size;
121 }
122
123
124 size_t
Access_file_copy(char * dest_file,char * source_file)125 Access_file_copy (char *dest_file, char *source_file) {
126 size_t nbytes = 0;
127 FILE *dest, *source;
128 int c;
129
130 if ((source = FOPEN_READ_BINARY(source_file)) == NULL) {
131 fprintf(stderr,"Cannot open source file %s\n",source_file);
132 return 0;
133
134 } else if ((dest = FOPEN_WRITE_BINARY(dest_file)) == NULL) {
135 fprintf(stderr,"Cannot open destination file %s\n",dest_file);
136 fclose(source);
137 return 0;
138
139 } else {
140 while ((c = fgetc(source)) != EOF) {
141 fputc(c,dest);
142 nbytes++;
143 }
144 fclose(dest);
145 fclose(source);
146 return nbytes;
147 }
148 }
149
150
151 bool
Access_file_equal(char * file1,char * file2)152 Access_file_equal (char *file1, char *file2) {
153 FILE *fp1, *fp2;
154 int c1, c2;
155
156 if ((fp1 = FOPEN_READ_BINARY(file1)) == NULL) {
157 fprintf(stderr,"Cannot open file %s\n",file1);
158 exit(9);
159
160 } else if ((fp2 = FOPEN_READ_BINARY(file2)) == NULL) {
161 fprintf(stderr,"Cannot open file %s\n",file2);
162 fclose(fp1);
163 exit(9);
164
165 } else {
166 c1 = fgetc(fp1);
167 c2 = fgetc(fp2);
168 while (c1 != EOF && c2 != EOF) {
169 if (c1 != c2) {
170 fclose(fp2);
171 fclose(fp1);
172 return false;
173 }
174 c1 = fgetc(fp1);
175 c2 = fgetc(fp2);
176 }
177 fclose(fp2);
178 fclose(fp1);
179
180 if (c1 == EOF && c2 == EOF) {
181 return true;
182 } else {
183 return false;
184 }
185 }
186 }
187
188
189 int
Access_fileio(char * filename)190 Access_fileio (char *filename) {
191 int fd;
192
193 if ((fd = open(filename,O_RDONLY,0764)) < 0) {
194 fprintf(stderr,"Error: can't open file %s with open for reading\n",filename);
195 exit(9);
196 }
197 return fd;
198 }
199
200 int
Access_fileio_rw(char * filename)201 Access_fileio_rw (char *filename) {
202 int fd;
203
204 if ((fd = open(filename,O_RDWR | O_CREAT | O_TRUNC,0764)) < 0) {
205 fprintf(stderr,"Error: can't open file %s with open for reading/writing\n",filename);
206 exit(9);
207 }
208 return fd;
209 }
210
211
212 #if 0
213 #ifndef WORDS_BIGENDIAN
214 /* Previously needed as a test on Macintosh machines */
215 static unsigned char
216 first_nonzero_char (size_t *i, char *filename) {
217 size_t len;
218 FILE *fp;
219 unsigned char value = 0;
220 void *p;
221
222 len = Access_filesize(filename);
223
224 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
225 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
226 exit(9);
227 } else {
228 *i = 0;
229 p = (void *) &value;
230 while ((size_t) *i < len && fread(p,sizeof(unsigned char),1,fp) > 0 &&
231 value == 0) {
232 *i += 1;
233 }
234 if (value == 0) {
235 *i = -1;
236 }
237 fclose(fp);
238 return value;
239 }
240 }
241
242 static UINT4
243 first_nonzero_uint (size_t *i, char *filename) {
244 size_t len;
245 FILE *fp;
246 UINT4 value = 0;
247 void *p;
248
249 len = Access_filesize(filename);
250
251 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
252 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
253 exit(9);
254 } else {
255 *i = 0;
256 p = (void *) &value;
257 while ((size_t) *i < len && fread(p,sizeof(UINT4),1,fp) > 0 &&
258 value == 0) {
259 *i += 1;
260 }
261 if (value == 0) {
262 *i = -1;
263 }
264 fclose(fp);
265 return value;
266 }
267 }
268
269 static UINT8
270 first_nonzero_uint8 (size_t *i, char *filename) {
271 size_t len;
272 FILE *fp;
273 UINT8 value = 0;
274 void *p;
275
276 len = Access_filesize(filename);
277 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
278 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
279 exit(9);
280 } else {
281 *i = 0;
282 p = (void *) &value;
283 while ((size_t) *i < len && fread(p,sizeof(UINT8),1,fp) > 0 &&
284 value == 0) {
285 *i += 1;
286 }
287 if (value == 0) {
288 *i = -1;
289 }
290 fclose(fp);
291 return value;
292 }
293 }
294 #endif
295 #endif
296
297
298 /************************************************************************
299 * Functions for shared memory and semaphores
300 ************************************************************************/
301
302 static List_T shmem_memory = NULL;
303 static Intlist_T shmem_ids = NULL;
304 static Intlist_T semaphore_ids = NULL;
305
306
307 #if 0
308 /* See if item is already in shared memory */
309 static bool
310 shmem_exists_p (int *shmid, key_t key) {
311 if ((*shmid = shmget(key,0,0)) == -1) {
312 return false;
313 } else {
314 return true;
315 }
316 }
317 #endif
318
319
320 static short
shmem_nattached(int shmid)321 shmem_nattached (int shmid) {
322 struct shmid_ds buf;
323
324 if (shmctl(shmid,IPC_STAT,&buf) == -1) {
325 #if 0
326 fprintf(stderr,"Error in shmem_nattached with shmctl. Error %d: %s\n",
327 errno,strerror(errno));
328 #endif
329 return -1;
330 } else {
331 return buf.shm_nattch;
332 }
333 }
334
335
336 #if 0
337 void
338 Access_shmem_remove (char *filename) {
339 key_t key;
340 int shmid, semid;
341 struct shmid_ds *buf = NULL;
342
343 key = ftok(filename,PROJECT_ID);
344 if (shmem_exists_p(&shmid,key) == false) {
345 /* Nothing to do */
346 } else if (shmctl(shmid,IPC_RMID,buf) == -1) {
347 fprintf(stderr,"Error with shmctl. Error %d: %s\n",errno,strerror(errno));
348 } else {
349 fprintf(stderr,"Successfully removed existing memory\n");
350 }
351
352 if ((semid = semget(key,/*nsems*/0,0)) == -1) {
353 /* Nothing to do */
354 } else {
355 fprintf(stderr,"Removing semaphore set %d\n",semid);
356 semctl(semid,SEMNO_NA,IPC_RMID,NULL);
357 }
358
359 return;
360 }
361 #endif
362
363 void
Access_controlled_cleanup()364 Access_controlled_cleanup () {
365 List_free(&shmem_memory);
366 Intlist_free(&shmem_ids);
367 Intlist_free(&semaphore_ids);
368 return;
369 }
370
371
372 void
Access_emergency_cleanup()373 Access_emergency_cleanup () {
374 List_T p;
375 Intlist_T r, q;
376 void *memory;
377 int shmid, semid;
378 int nattached;
379 struct shmid_ds *buf = NULL;
380
381 fprintf(stderr,"Calling Access_emergency_cleanup\n");
382
383 /* First, detach all memory */
384 for (p = shmem_memory; p != NULL; p = List_next(p)) {
385 memory = List_head(p);
386 if (shmdt(memory) == -1) {
387 #if 0
388 /* Somehow, shmdt forks and prints the error message and continues with the rest of the code */
389 fprintf(stderr,"Error in Access_emergency_cleanup with shmdt on memory %p, shmid %d. Error %d: %s\n",
390 memory,shmid,errno,strerror(errno));
391 #endif
392 }
393 }
394
395 if (unload_shared_memory_p == true) {
396 /* Don't try to fix */
397
398 } else if (preload_shared_memory_p == true) {
399 /* Don't try to fix */
400
401 } else {
402 /* Delete memory and semaphores */
403 for (r = semaphore_ids, q = shmem_ids; r != NULL; r = Intlist_next(r), q = Intlist_next(q)) {
404 semid = Intlist_head(r);
405 shmid = Intlist_head(q);
406
407 if (Semaphore_get_value(semid,SEMNO_KEEP) == SEMAPHORE_RESIDENT) {
408 /* Keep memory resident */
409 } else if ((nattached = shmem_nattached(shmid)) > 0) {
410 /* Keep memory resident */
411 } else if (shmctl(shmid,IPC_RMID,buf) == -1) {
412 /* Error in deleting memory, possibly with multiple threads trying to delete */
413 /* fprintf(stderr,"Error in Access_emergency_cleanup with shmctl. Error %d: %s\n",
414 errno,strerror(errno)); */
415 /* Semaphore_delete(semid); */
416 } else {
417 fprintf(stderr,"Removed existing memory for shmid %d\n",shmid);
418 Semaphore_delete(semid);
419 }
420 }
421 }
422
423 if (shmem_memory != NULL) {
424 fprintf(stderr,"\n");
425 fprintf(stderr,"You may want to run 'ipcs -m' to see if any shared memory segments are still in use\n");
426 fprintf(stderr,"You can remove a shared memory segment manually by doing 'ipcrm -m <shmid>'\n");
427 fprintf(stderr,"\n");
428 }
429
430 List_free(&shmem_memory);
431 Intlist_free(&shmem_ids);
432 Intlist_free(&semaphore_ids);
433
434 return;
435 }
436
437
438 void
Access_deallocate(void * memory,int shmid,key_t key)439 Access_deallocate (void *memory, int shmid, key_t key) {
440 struct shmid_ds *buf = NULL;
441 short nattached;
442 int semid;
443
444 /* First, detach memory */
445 if (shmdt(memory) == -1) {
446 #if 0
447 /* Somehow, shmdt forks and prints the error message and continues with the rest of the code */
448 fprintf(stderr,"Error in Access_emergency_cleanup with shmdt on memory %p, shmid %d. Error %d: %s\n",
449 memory,shmid,errno,strerror(errno));
450 #endif
451 }
452
453 /* Then, delete memory and semaphores, if applicable */
454 if ((semid = Semaphore_find(key)) != -1 && Semaphore_get_value(semid,SEMNO_KEEP) == SEMAPHORE_RESIDENT) {
455 fprintf(stderr,"Keeping memory for shmid %d resident, because it was pre-loaded. To remove, run gsnap on this genome index with --unload-shared-memory\n",
456 shmid);
457
458 } else if ((nattached = shmem_nattached(shmid)) > 0) {
459 fprintf(stderr,"Keeping memory for shmid %d resident, because it is being used by %d processes\n",
460 shmid,nattached);
461
462 } else if (shmctl(shmid,IPC_RMID,buf) == -1) {
463 /* Somehow, shmctl forks and prints the error message and continues with the rest of the code */
464 /* fprintf(stderr,"Error in Access_deallocate with shmctl. Error %d: %s\n",errno,strerror(errno)); */
465 /* Semaphore_delete(semid); */
466
467 } else {
468 fprintf(stderr,"Removed existing memory for shmid %d\n",shmid);
469 Semaphore_delete(semid);
470 }
471
472 return;
473 }
474
475
476 /* Same as Access_deallocate, but without deleting semaphore */
477 void
Access_deallocate_wo_semaphore(void * memory,int shmid,key_t key)478 Access_deallocate_wo_semaphore (void *memory, int shmid, key_t key) {
479 struct shmid_ds *buf = NULL;
480 short nattached;
481 int semid;
482
483 /* First, detach memory */
484 if (shmdt(memory) == -1) {
485 #if 0
486 /* Somehow, shmdt forks and prints the error message and continues with the rest of the code */
487 fprintf(stderr,"Error in Access_emergency_cleanup with shmdt on memory %p, shmid %d. Error %d: %s\n",
488 memory,shmid,errno,strerror(errno));
489 #endif
490 }
491
492 /* Then, delete memory and semaphores, if applicable */
493 if ((semid = Semaphore_find(key)) != -1 && Semaphore_get_value(semid,SEMNO_KEEP) == SEMAPHORE_RESIDENT) {
494 fprintf(stderr,"Keeping memory for shmid %d resident, because it was pre-loaded. To remove, run gsnap on this genome index with --unload-shared-memory\n",
495 shmid);
496
497 } else if ((nattached = shmem_nattached(shmid)) > 0) {
498 fprintf(stderr,"Keeping memory for shmid %d resident, because it is being used by %d processes\n",
499 shmid,nattached);
500
501 } else if (shmctl(shmid,IPC_RMID,buf) == -1) {
502 /* Somehow, shmctl forks and prints the error message and continues with the rest of the code */
503 /* fprintf(stderr,"Error in Access_deallocate with shmctl. Error %d: %s\n",errno,strerror(errno)); */
504 /* Semaphore_delete(semid); */
505
506 } else {
507 fprintf(stderr,"Removed existing memory for shmid %d\n",shmid);
508 /* Semaphore_delete(semid); */
509 }
510
511 return;
512 }
513
514
515
516 #define FREAD_BATCH 100000000 /* 100 million elements at a time */
517
518 static void
copy_memory_from_file(void * memory,char * filename,size_t filesize,size_t eltsize)519 copy_memory_from_file (void *memory, char *filename, size_t filesize, size_t eltsize) {
520 FILE *fp;
521 void *p;
522 size_t i;
523
524 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
525 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
526 exit(9);
527 }
528
529 if (eltsize == 1) {
530 for (i = 0; i + FREAD_BATCH < filesize/eltsize; i += FREAD_BATCH) {
531 p = (void *) &(((unsigned char *) memory)[i]);
532 fread(p,sizeof(unsigned char),FREAD_BATCH,fp);
533 }
534
535 if (i < filesize/eltsize) {
536 p = (void *) &(((unsigned char *) memory)[i]);
537 fread(p,sizeof(unsigned char),filesize/eltsize - i,fp);
538 }
539
540 } else if (eltsize == 2) {
541 for (i = 0; i + FREAD_BATCH < filesize/eltsize; i += FREAD_BATCH) {
542 p = (void *) &(((UINT2 *) memory)[i]);
543 fread(p,sizeof(UINT2),FREAD_BATCH,fp);
544 }
545
546 if (i < filesize/eltsize) {
547 p = (void *) &(((UINT2 *) memory)[i]);
548 fread(p,sizeof(UINT2),filesize/eltsize - i,fp);
549 }
550
551 } else if (eltsize == 4) {
552 for (i = 0; i + FREAD_BATCH < filesize/eltsize; i += FREAD_BATCH) {
553 p = (void *) &(((UINT4 *) memory)[i]);
554 fread(p,sizeof(UINT4),FREAD_BATCH,fp);
555 }
556
557 if (i < filesize/eltsize) {
558 p = (void *) &(((UINT4 *) memory)[i]);
559 fread(p,sizeof(UINT4),filesize/eltsize - i,fp);
560 }
561
562 } else if (eltsize == 8) {
563 for (i = 0; i + FREAD_BATCH < filesize/eltsize; i += FREAD_BATCH) {
564 p = (void *) &(((UINT8 *) memory)[i]);
565 fread(p,sizeof(UINT8),FREAD_BATCH,fp);
566 }
567
568 if (i < filesize/eltsize) {
569 p = (void *) &(((UINT8 *) memory)[i]);
570 fread(p,sizeof(UINT8),filesize/eltsize - i,fp);
571 }
572
573 } else {
574 fprintf(stderr,"Access_allocated called with an element size of %d, which is not handled\n",(int) eltsize);
575 exit(9);
576 }
577 fclose(fp);
578
579 return;
580 }
581
582
583 static void
copy_limited_from_file(void * memory,char * filename,size_t filesize,size_t eltsize)584 copy_limited_from_file (void *memory, char *filename, size_t filesize, size_t eltsize) {
585 FILE *fp;
586 void *p;
587
588 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
589 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
590 exit(9);
591 }
592
593 if (eltsize == 1) {
594 p = (void *) &(((unsigned char *) memory)[0]);
595 if (FREAD_BATCH < filesize/eltsize) {
596 fread(p,sizeof(unsigned char),FREAD_BATCH,fp);
597 } else {
598 fread(p,sizeof(unsigned char),filesize/eltsize,fp);
599 }
600
601 } else if (eltsize == 2) {
602 p = (void *) &(((UINT2 *) memory)[0]);
603 if (FREAD_BATCH < filesize/eltsize) {
604 fread(p,sizeof(UINT2),FREAD_BATCH,fp);
605 } else {
606 fread(p,sizeof(UINT2),filesize/eltsize,fp);
607 }
608
609 } else if (eltsize == 4) {
610 p = (void *) &(((UINT4 *) memory)[0]);
611 if (FREAD_BATCH < filesize/eltsize) {
612 fread(p,sizeof(UINT4),FREAD_BATCH,fp);
613 } else {
614 fread(p,sizeof(UINT4),filesize/eltsize,fp);
615 }
616
617 } else if (eltsize == 8) {
618 p = (void *) &(((UINT8 *) memory)[0]);
619 if (FREAD_BATCH < filesize/eltsize) {
620 fread(p,sizeof(UINT8),FREAD_BATCH,fp);
621 } else {
622 fread(p,sizeof(UINT8),filesize/eltsize,fp);
623 }
624
625 } else {
626 fprintf(stderr,"Access_allocated called with an element size of %d, which is not handled\n",(int) eltsize);
627 exit(9);
628 }
629 fclose(fp);
630
631 return;
632 }
633
634
635 static void *
shmem_attach(int * shmid,key_t * key,char * filename,size_t filesize,size_t eltsize)636 shmem_attach (int *shmid, key_t *key, char *filename, size_t filesize, size_t eltsize) {
637 void *memory = NULL;
638 int semid = -1;
639 ushort values[NSEMAPHORES];
640 int nattached;
641 bool donep;
642 int niter;
643
644 values[SEMNO_LOCK] = -1;
645 /* For some reason, these values are not being set, so using Semaphore_set_value below */
646 if (preload_shared_memory_p == true) {
647 values[SEMNO_KEEP] = SEMAPHORE_RESIDENT;
648 } else {
649 values[SEMNO_KEEP] = SEMAPHORE_FREEABLE;
650 }
651
652 *key = ftok(filename,PROJECT_ID);
653 while (semid == -1) {
654 if ((semid = Semaphore_create(*key,/*nsems*/NSEMAPHORES,values)) != -1) {
655 /* Created a new semaphore */
656 if (preload_shared_memory_p == true) {
657 Semaphore_set_value(semid,SEMNO_KEEP,SEMAPHORE_RESIDENT);
658 }
659
660 } else if ((semid = Semaphore_find(*key)) != -1) {
661 /* Got existing semaphore */
662 if (unload_shared_memory_p == true) {
663 Semaphore_set_value(semid,SEMNO_KEEP,SEMAPHORE_FREEABLE);
664 }
665
666 #if 0
667 if (Semaphore_get_value(semid,SEMNO_KEEP) == SEMNO_RESIDENT) {
668 /* Semaphore exists only to keep memory resident */
669 fprintf(stderr,"Using shared memory already resident in system\n");
670
671 } else {
672 /* Semaphore being created */
673 semaphore_wait_for_creation(semid);
674 }
675 #endif
676 }
677 }
678
679 /* The process that created the semaphore will proceed, while the
680 others wait. They will be woken up when the semaphore is
681 removed. */
682
683 donep = false;
684 niter = 0;
685 while (donep == false) {
686 if ((*shmid = shmget(*key,filesize,IPC_CREAT | IPC_EXCL |
687 #ifdef HAVE_SHM_NORESERVE
688 SHM_NORESERVE |
689 #endif
690 0666)) != -1) {
691 /* Created new shared memory */
692 if ((memory = shmat(*shmid,NULL,0)) == (void *) -1) {
693 fprintf(stderr,"Error with shmat (1). Error %d: %s\n",errno,strerror(errno));
694 exit(9);
695
696 } else if (unload_shared_memory_p == true) {
697 semaphore_ids = Intlist_push(semaphore_ids,semid);
698 shmem_memory = List_push(shmem_memory,memory);
699 shmem_ids = Intlist_push(shmem_ids,*shmid);
700 copy_limited_from_file(memory,filename,filesize,eltsize);
701 donep = true;
702
703 } else {
704 semaphore_ids = Intlist_push(semaphore_ids,semid);
705 shmem_memory = List_push(shmem_memory,memory);
706 shmem_ids = Intlist_push(shmem_ids,*shmid);
707 copy_memory_from_file(memory,filename,filesize,eltsize);
708 fprintf(stderr,"Attached new memory for %s...",filename);
709 donep = true;
710 }
711
712 } else if ((*shmid = shmget(*key,0,0)) != -1) {
713 /* Found existing shared memory */
714 if ((memory = shmat(*shmid,NULL,0)) == (void *) -1) {
715 fprintf(stderr,"Error with shmat (2). Error %d: %s\n",errno,strerror(errno));
716 exit(9);
717
718 } else if ((nattached = shmem_nattached(*shmid)) > 1) {
719 /* Existing memory is indeed valid: attached (by a process other than this one) */
720 semaphore_ids = Intlist_push(semaphore_ids,semid);
721 shmem_memory = List_push(shmem_memory,memory);
722 shmem_ids = Intlist_push(shmem_ids,*shmid);
723 fprintf(stderr,"Attached existing memory (%d attached) for %s...",nattached,filename);
724 donep = true;
725
726 } else if (niter > MAX_KILL_ATTEMPTS) {
727 /* Existing memory is not attached by any other process, but unable to kill. Could be corrupted */
728 fprintf(stderr,"Abandoned shared memory found, but could not be removed after %d attempts. Using malloc instead of shared memory\n",niter);
729 memory = (void *) NULL;
730 donep = true;
731
732 } else {
733 /* Existing memory is not attached by any other process. Could be corrupted. */
734 fprintf(stderr,"Clearing existing shared memory for %s...\n",filename);
735 Access_deallocate_wo_semaphore(memory,*shmid,*key);
736
737 /* Now loop above to try again */
738 }
739
740 } else {
741 fprintf(stderr,"Using malloc instead of shmget for file %s\n",filename);
742 memory = (void *) NULL;
743 donep = true;
744 }
745
746 niter++;
747 }
748
749 #if 0
750 /* The process that proceeded removes the semaphore here, allowing
751 the other processes to continue after their waits. The other
752 processes will try to remove the semaphore too, yielding an
753 error, which we simply ignore. */
754 if (unload_shared_memory_p == true) {
755 /* Deleting semaphore will allow Access_deallocate to remove the shared memory */
756 Semaphore_delete(semid);
757
758 } else if (Semaphore_get_value(semid,SEMNO_KEEP) == SEMAPHORE_RESIDENT) {
759 /* Exists to keep memory resident */
760 fprintf(stderr,"Semaphore %d intended to keep memory resident\n",semid);
761
762 } else if (preload_shared_memory_p == false) {
763 Semaphore_delete(semid);
764
765 } else {
766 /* Keep semaphore in system, but indicate we are done with creation */
767 /* fprintf(stderr,"Forcing memory for %d to stay resident in system\n",*shmid); */
768 #if 0
769 semaphore_set_value(semid,SEMAPHORE_KEEP_RESIDENT,/*value*/0);
770 #endif
771 }
772 #endif
773
774
775 return memory;
776 }
777
778
779 /* Bigendian conversion not needed after this */
780 void *
Access_allocate_private(Access_T * access,size_t * len,double * seconds,char * filename,size_t eltsize)781 Access_allocate_private (Access_T *access, size_t *len, double *seconds, char *filename, size_t eltsize) {
782 void *memory;
783 #ifdef CHECK
784 void *memory2;
785 #endif
786 #if 0 && defined (USE_MPI)
787 /* Does not work. Gets ftruncate error */
788 MPI_Comm comm;
789 MPI_Win win;
790 #endif
791 Stopwatch_T stopwatch;
792
793 assert(filename != NULL);
794
795 *len = Access_filesize(filename);
796 if (*len == 0) {
797 *seconds = 0.0;
798 return (void *) NULL;
799 }
800
801 Stopwatch_start(stopwatch = Stopwatch_new());
802
803 #ifdef CHECK
804 memory2 = (void *) MALLOC(*len);
805 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
806 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
807 exit(9);
808 }
809
810 if (eltsize == 1) {
811 FREAD_CHARS(memory2,(*len)/eltsize,fp);
812 } else if (eltsize == 2) {
813 FREAD_USHORTS(memory2,(*len)/eltsize,fp);
814 } else if (eltsize == 4) {
815 FREAD_UINTS(memory2,(*len)/eltsize,fp);
816 } else if (eltsize == 8) {
817 FREAD_UINT8S(memory2,(*len)/eltsize,fp);
818 } else {
819 fprintf(stderr,"Access_allocated called with an element size of %d, which is not handled\n",(int) eltsize);
820 exit(9);
821 }
822 fclose(fp);
823 #endif
824
825 memory = (void *) MALLOC(*len);
826 copy_memory_from_file(memory,filename,/*filesize*/*len,eltsize);
827 *access = ALLOCATED_PRIVATE;
828
829 /* Note: the following (old non-batch mode) requires conversion to bigendian later, as needed */
830 /* fread(new->offsets,eltsize,sb.st_size/eltsize,fp); */
831
832 #ifdef CHECK
833 for (i = 0; i < *len; i++) {
834 if (((unsigned char *) memory)[i] != ((unsigned char *) memory2)[i]) {
835 abort();
836 }
837 }
838 FREE(memory2);
839 #endif
840
841 *seconds = Stopwatch_stop(stopwatch);
842 Stopwatch_free(&stopwatch);
843
844 return memory;
845 }
846
847
848 /* Bigendian conversion not needed after this */
849 void *
Access_allocate_shared(Access_T * access,int * shmid,key_t * key,int * fd,size_t * len,double * seconds,char * filename,size_t eltsize)850 Access_allocate_shared (Access_T *access, int *shmid, key_t *key, int *fd, size_t *len, double *seconds, char *filename, size_t eltsize) {
851 void *memory;
852 #ifdef CHECK
853 void *memory2;
854 #endif
855 #if 0 && defined (USE_MPI)
856 /* Does not work. Gets ftruncate error */
857 MPI_Comm comm;
858 MPI_Win win;
859 #endif
860 Stopwatch_T stopwatch;
861
862 assert(filename != NULL);
863
864 *len = (size_t) Access_filesize(filename);
865 if (*len == 0) {
866 *seconds = 0.0;
867 return (void *) NULL;
868 }
869
870 Stopwatch_start(stopwatch = Stopwatch_new());
871
872 #ifdef CHECK
873 memory2 = (void *) MALLOC(*len);
874 if ((fp = FOPEN_READ_BINARY(filename)) == NULL) {
875 fprintf(stderr,"Error: can't open file %s with fopen\n",filename);
876 exit(9);
877 }
878
879 if (eltsize == 1) {
880 FREAD_CHARS(memory2,(*len)/eltsize,fp);
881 } else if (eltsize == 2) {
882 FREAD_USHORTS(memory2,(*len)/eltsize,fp);
883 } else if (eltsize == 4) {
884 FREAD_UINTS(memory2,(*len)/eltsize,fp);
885 } else if (eltsize == 8) {
886 FREAD_UINT8S(memory2,(*len)/eltsize,fp);
887 } else {
888 fprintf(stderr,"Access_allocated called with an element size of %d, which is not handled\n",(int) eltsize);
889 exit(9);
890 }
891 fclose(fp);
892 #endif
893
894 #if 0 && defined(USE_MPI)
895 /* Does not work. Gives ftruncate error */
896 MPI_Comm_split_type(MPI_COMM_WORLD,MPI_COMM_TYPE_SHARED,0,MPI_INFO_NULL,&comm);
897 MPI_Win_allocate_shared(*len,/*disp_unit*/1,MPI_INFO_NULL,comm,&memory,&win);
898 MPI_Win_free(&win);
899 #elif defined(HAVE_MMAP)
900 if ((memory = shmem_attach(&(*shmid),&(*key),filename,/*filesize*/*len,eltsize)) != NULL) {
901 *access = ALLOCATED_SHARED;
902 } else {
903 fprintf(stderr,"shm_attach not working on file %s, so using memory mapping instead on %lu bytes\n",
904 filename,*len);
905 *shmid = 0;
906 memory = Access_mmap(&(*fd),&(*len),&(*seconds),filename,/*randomp*/true);
907 #if 0
908 /* Crashes because memory is read-only */
909 copy_memory_from_file(memory,filename,/*filesize*/*len,eltsize);
910 #endif
911 *access = MMAPPED;
912 }
913 #else
914 if ((memory = shmem_attach(&(*shmid),&(*key),filename,/*filesize*/*len,eltsize)) != NULL) {
915 *access = ALLOCATED_SHARED;
916 } else {
917 fprintf(stderr,"shm_attach not working on file %s, so using malloc instead on %lu bytes\n",
918 filename,*len);
919 *shmid = 0;
920 memory = (void *) MALLOC(*len);
921 copy_memory_from_file(memory,filename,/*filesize*/*len,eltsize);
922 *access = ALLOCATED_PRIVATE;
923 }
924 #endif
925
926 /* Note: the following (old non-batch mode) requires conversion to bigendian later, as needed */
927 /* fread(new->offsets,eltsize,sb.st_size/eltsize,fp); */
928
929 #ifdef CHECK
930 for (i = 0; i < *len; i++) {
931 if (((unsigned char *) memory)[i] != ((unsigned char *) memory2)[i]) {
932 abort();
933 }
934 }
935 FREE(memory2);
936 #endif
937
938 if (*access == MMAPPED) {
939 /* Already computed seconds */
940 } else {
941 *seconds = Stopwatch_stop(stopwatch);
942 }
943 Stopwatch_free(&stopwatch);
944
945 return memory;
946 }
947
948
949 #define PAGESIZE 1024*4
950
951 static int
get_pagesize()952 get_pagesize () {
953
954 #ifdef PAGESIZE_VIA_SYSCTL
955 int pagesize;
956 size_t pagelen;
957 int mib[2];
958 #endif
959
960 #ifdef __STRICT_ANSI__
961 return PAGESIZE;
962 #elif defined(HAVE_GETPAGESIZE)
963 return getpagesize();
964 #elif defined(PAGESIZE_VIA_SYSCONF)
965 return (int) sysconf(_SC_PAGESIZE);
966 #elif defined(PAGESIZE_VIA_SYSCTL)
967 pagelen = sizeof(pagesize);
968 mib[0] = CTL_HW;
969 mib[1] = HW_PAGESIZE;
970 sysctl(mib,2,&pagesize,&pagelen,NULL,0);
971 return pagesize;
972 #else
973 return PAGESIZE;
974 #endif
975
976 }
977
978
979 #ifdef HAVE_MMAP
980 /* Returns NULL if mmap fails. Bigendian conversion required */
981 #ifdef HAVE_CADDR_T
982 caddr_t
983 #else
984 void *
985 #endif
Access_mmap(int * fd,size_t * len,double * seconds,char * filename,bool randomp)986 Access_mmap (int *fd, size_t *len, double *seconds, char *filename, bool randomp) {
987 size_t length;
988 #ifdef HAVE_CADDR_T
989 caddr_t memory;
990 #else
991 void *memory;
992 #endif
993 Stopwatch_T stopwatch;
994
995 Stopwatch_start(stopwatch = Stopwatch_new());
996
997 if (filename == NULL) {
998 abort();
999
1000 } else if ((*len = length = Access_filesize(filename)) == 0U) {
1001 fprintf(stderr,"Warning: file %s is empty\n",filename);
1002 *fd = open(filename,O_RDONLY,0764); /* Still need to initialize value */
1003 memory = (void *) NULL;
1004
1005 } else if ((*fd = open(filename,O_RDONLY,0764)) < 0) {
1006 fprintf(stderr,"Error: can't open file %s with open for reading\n",filename);
1007 exit(9);
1008
1009 } else if (sizeof(size_t) <= 4 && length > MAX32BIT) {
1010 debug(printf("Too big to mmap\n"));
1011 *len = 0;
1012 memory = (void *) NULL;
1013
1014 } else {
1015 *len = length;
1016 memory = mmap(NULL,length,PROT_READ,0
1017 #ifdef HAVE_MMAP_MAP_PRIVATE
1018 |MAP_PRIVATE
1019 #endif
1020 #ifdef HAVE_MMAP_MAP_FILE
1021 |MAP_FILE
1022 #endif
1023 #ifdef HAVE_MMAP_MAP_VARIABLE
1024 |MAP_VARIABLE
1025 #endif
1026 /*|MAP_NORESERVE*/
1027 ,*fd,0);
1028
1029 if (memory == MAP_FAILED) {
1030 fprintf(stderr,"Error in access.c (1): Got mmap failure on file %s with length %jd. Error %d: %s\n",
1031 filename,length,errno,strerror(errno));
1032 debug(printf("Got MAP_FAILED on file %s with length %jd\n",filename,length));
1033 memory = (void *) NULL;
1034
1035 } else if (randomp == true) {
1036 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1037 #ifdef HAVE_MADVISE
1038 #ifdef HAVE_MADVISE_MADV_RANDOM
1039 madvise(memory,*len,MADV_RANDOM);
1040 #endif
1041 #endif
1042
1043 } else {
1044 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1045 #ifdef HAVE_MADVISE
1046 #ifdef HAVE_MADVISE_MADV_DONTNEED
1047 madvise(memory,*len,MADV_DONTNEED);
1048 #endif
1049 #endif
1050 }
1051 }
1052
1053 *seconds = Stopwatch_stop(stopwatch);
1054 Stopwatch_free(&stopwatch);
1055
1056 return memory;
1057 }
1058 #endif
1059
1060
1061
1062 #ifdef HAVE_MMAP
1063 /* Returns NULL if mmap fails. Bigendian conversion required */
1064 #ifdef HAVE_CADDR_T
1065 caddr_t
1066 #else
1067 void *
1068 #endif
Access_mmap_offset(int * remainder,int fd,size_t offset,size_t length,bool randomp)1069 Access_mmap_offset (int *remainder, int fd, size_t offset, size_t length, bool randomp) {
1070 #ifdef HAVE_CADDR_T
1071 caddr_t memory;
1072 #else
1073 void *memory;
1074 #endif
1075
1076 if (length == 0) {
1077 abort();
1078 }
1079
1080 *remainder = offset % get_pagesize();
1081 offset -= (size_t) *remainder;
1082 length += (size_t) *remainder;
1083
1084 if (sizeof(size_t) <= 4 && length > MAX32BIT) {
1085 debug(printf("Too big to mmap\n"));
1086 memory = (void *) NULL;
1087 } else {
1088 memory = mmap(NULL,length,PROT_READ,0
1089 #ifdef HAVE_MMAP_MAP_PRIVATE
1090 |MAP_PRIVATE
1091 #endif
1092 #ifdef HAVE_MMAP_MAP_FILE
1093 |MAP_FILE
1094 #endif
1095 #ifdef HAVE_MMAP_MAP_VARIABLE
1096 |MAP_VARIABLE
1097 #endif
1098 /*|MAP_NORESERVE*/
1099 ,fd,offset);
1100
1101 if (memory == MAP_FAILED) {
1102 fprintf(stderr,"Error in access.c (2): Got mmap failure on fd %d, offset %jd, length %jd. Error %d: %s\n",
1103 fd,offset,length,errno,strerror(errno));
1104 debug(printf("Got MAP_FAILED on fd %d, offset %jd, length %zu\n",fd,offset,length));
1105 memory = (void *) NULL;
1106
1107 } else if (randomp == true) {
1108 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1109 #ifdef HAVE_MADVISE
1110 #ifdef HAVE_MADVISE_MADV_RANDOM
1111 madvise(memory,length,MADV_RANDOM);
1112 #endif
1113 #endif
1114
1115 } else {
1116 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1117 #ifdef HAVE_MADVISE
1118 #ifdef HAVE_MADVISE_MADV_DONTNEED
1119 madvise(memory,length,MADV_DONTNEED);
1120 #endif
1121 #endif
1122 }
1123 }
1124
1125 return memory;
1126 }
1127 #endif
1128
1129
1130 #if 0
1131 /* All programs are read-only */
1132 #ifdef HAVE_MMAP
1133 /* Returns NULL if mmap fails. Bigendian conversion required */
1134 #ifdef HAVE_CADDR_T
1135 caddr_t
1136 #else
1137 void *
1138 #endif
1139 Access_mmap_rw (int *fd, size_t *len, char *filename, bool randomp) {
1140 size_t length;
1141 #ifdef HAVE_CADDR_T
1142 caddr_t memory;
1143 #else
1144 void *memory;
1145 #endif
1146
1147 if ((*len = length = Access_filesize(filename)) == 0U) {
1148 fprintf(stderr,"Warning: file %s is empty\n",filename);
1149 *fd = open(filename,O_RDWR,0764); /* Still need to initialize value */
1150 memory = (void *) NULL;
1151 } else if ((*fd = open(filename,O_RDWR,0764)) < 0) {
1152 fprintf(stderr,"Error: can't open file %s with open for reading/writing\n",filename);
1153 exit(9);
1154 } else if (sizeof(size_t) <= 4 && length > MAX32BIT) {
1155 debug(printf("Too big to mmap\n"));
1156 *len = 0;
1157 memory = (void *) NULL;
1158 } else {
1159 *len = length;
1160 memory = mmap(NULL,length,PROT_READ|PROT_WRITE,0
1161 #ifdef HAVE_MMAP_MAP_SHARED
1162 |MAP_SHARED
1163 #endif
1164 #ifdef HAVE_MMAP_MAP_FILE
1165 |MAP_FILE
1166 #endif
1167 #ifdef HAVE_MMAP_MAP_VARIABLE
1168 |MAP_VARIABLE
1169 #endif
1170 /*|MAP_NORESERVE*/
1171 ,*fd,0);
1172
1173 if (memory == MAP_FAILED) {
1174 fprintf(stderr,"Error in access.c (3): Got mmap failure on file %s with len %jd from length %jd. Error %d: %s\n",
1175 filename,*len,length,errno,strerror(errno));
1176 debug(printf("Got MAP_FAILED on file %s with len %zu from length %jd\n",filename,*len,length));
1177 memory = (void *) NULL;
1178
1179 } else if (randomp == true) {
1180 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1181 #ifdef HAVE_MADVISE
1182 #ifdef HAVE_MADVISE_MADV_RANDOM
1183 madvise(memory,*len,MADV_RANDOM);
1184 #endif
1185 #endif
1186
1187 } else {
1188 debug(printf("Got mmap of %jd bytes at %p to %p\n",length,memory,memory+length-1));
1189 #ifdef HAVE_MADVISE
1190 #ifdef HAVE_MADVISE_MADV_DONTNEED
1191 madvise(memory,*len,MADV_DONTNEED);
1192 #endif
1193 #endif
1194 }
1195 }
1196
1197 return memory;
1198 }
1199 #endif
1200 #endif /* if 0 */
1201
1202
1203 #if 0
1204 /* All programs are read-only */
1205 #ifdef HAVE_MMAP
1206 /* Returns NULL if mmap fails. Bigendian conversion required */
1207 #ifdef HAVE_CADDR_T
1208 caddr_t
1209 #else
1210 void *
1211 #endif
1212 Access_mmap_offset_rw (int *remainder, int fd, size_t offset, size_t length, bool randomp) {
1213 #ifdef HAVE_CADDR_T
1214 caddr_t memory;
1215 #else
1216 void *memory;
1217 #endif
1218
1219 if (length == 0) {
1220 abort();
1221 }
1222
1223 *remainder = offset % get_pagesize();
1224 offset -= (size_t) *remainder;
1225 length += (size_t) *remainder;
1226
1227 if (sizeof(size_t) <= 4 && length > MAX32BIT) {
1228 debug(printf("Too big to mmap\n"));
1229 memory = (void *) NULL;
1230 } else {
1231 memory = mmap(NULL,length,PROT_READ|PROT_WRITE,0
1232 #ifdef HAVE_MMAP_MAP_SHARED
1233 |MAP_SHARED
1234 #endif
1235 #ifdef HAVE_MMAP_MAP_FILE
1236 |MAP_FILE
1237 #endif
1238 #ifdef HAVE_MMAP_MAP_VARIABLE
1239 |MAP_VARIABLE
1240 #endif
1241 /*|MAP_NORESERVE*/
1242 ,fd,offset);
1243
1244 if (memory == MAP_FAILED) {
1245 fprintf(stderr,"Error in access.c (4): Got mmap failure on offset %jd, length %jd. Error %d: %s\n",
1246 offset,length,errno,strerror(errno));
1247 debug(printf("Got MAP_FAILED on offset %jd, length %zu\n",offset,length));
1248 memory = (void *) NULL;
1249
1250 } else if (randomp == true) {
1251 debug(printf("Got mmap of %zu bytes at %p to %p\n",length,memory,memory+length-1));
1252 #ifdef HAVE_MADVISE
1253 #ifdef HAVE_MADVISE_MADV_RANDOM
1254 madvise(memory,length,MADV_RANDOM);
1255 #endif
1256 #endif
1257
1258 } else {
1259 debug(printf("Got mmap of %zu bytes at %p to %p\n",length,memory,memory+length-1));
1260 #ifdef HAVE_MADVISE
1261 #ifdef HAVE_MADVISE_MADV_DONTNEED
1262 madvise(memory,length,MADV_DONTNEED);
1263 #endif
1264 #endif
1265 }
1266 }
1267
1268 return memory;
1269 }
1270 #endif
1271 #endif /* if 0 */
1272
1273
1274
1275 #ifdef HAVE_MMAP
1276
1277 #ifdef HAVE_CADDR_T
1278 caddr_t
1279 #else
1280 void *
1281 #endif
Access_mmap_and_preload(int * fd,size_t * len,int * npages,double * seconds,char * filename,size_t eltsize)1282 Access_mmap_and_preload (int *fd, size_t *len, int *npages, double *seconds, char *filename, size_t eltsize) {
1283 size_t length;
1284 #ifdef HAVE_CADDR_T
1285 caddr_t memory;
1286 #else
1287 void *memory;
1288 #endif
1289 int pagesize, indicesperpage;
1290 size_t totalindices, i; /* Needs to handle uncompressed genomes > 2 gigabytes */
1291 int nzero = 0, npos = 0;
1292 Stopwatch_T stopwatch;
1293
1294
1295 if (filename == NULL) {
1296 abort();
1297
1298 } else if ((*len = length = Access_filesize(filename)) == 0U) {
1299 fprintf(stderr,"Warning: file %s is empty\n",filename);
1300 *fd = open(filename,O_RDONLY,0764); /* Still need to initialize value */
1301 memory = (void *) NULL;
1302
1303 } else if ((*fd = open(filename,O_RDONLY,0764)) < 0) {
1304 fprintf(stderr,"Error: can't open file %s with open for reading\n",filename);
1305 exit(9);
1306
1307 } else if (sizeof(size_t) <= 4 && *len > MAX32BIT) {
1308 debug(printf("Too big to mmap\n"));
1309 *len = 0;
1310 *npages = 0;
1311 *seconds = 0.0;
1312 memory = (void *) NULL;
1313
1314 } else {
1315 pagesize = get_pagesize();
1316
1317 indicesperpage = pagesize/eltsize;
1318
1319 Stopwatch_start(stopwatch = Stopwatch_new());
1320
1321 memory = mmap(NULL,length,PROT_READ,0
1322 #ifdef HAVE_MMAP_MAP_PRIVATE
1323 |MAP_PRIVATE
1324 #endif
1325 #ifdef HAVE_MMAP_MAP_FILE
1326 |MAP_FILE
1327 #endif
1328 #ifdef HAVE_MMAP_MAP_VARIABLE
1329 |MAP_VARIABLE
1330 #endif
1331 /*|MAP_NORESERVE*/
1332 ,*fd,0);
1333
1334 if (memory == MAP_FAILED) {
1335 fprintf(stderr,"Error in access.c (5): Got mmap failure on file %s with len %jd from length %jd. Error %d: %s\n",
1336 filename,*len,length,errno,strerror(errno));
1337 debug(printf("Got MAP_FAILED on file %s with len %jd from length %zu\n",filename,*len,length));
1338 memory = (void *) NULL;
1339 Stopwatch_stop(stopwatch);
1340 Stopwatch_free(&stopwatch);
1341
1342 } else {
1343 /* Touch all pages */
1344 debug(printf("Got mmap of %zu bytes at %p to %p\n",length,memory,memory+length-1));
1345 #ifdef HAVE_MADVISE
1346 #ifdef HAVE_MADVISE_MADV_WILLNEED
1347 madvise(memory,*len,MADV_WILLNEED);
1348 #endif
1349 #endif
1350 totalindices = (*len)/eltsize;
1351 fprintf(stderr,"...");
1352 for (i = 0; i < totalindices; i += indicesperpage) {
1353 if (((char *) memory)[i] == 0) {
1354 nzero++;
1355 #if 0
1356 if (i % 10000 == 0) {
1357 fprintf(stderr,",");
1358 }
1359 #endif
1360 } else {
1361 npos++;
1362 }
1363 #if 0
1364 if (i % 10000 == 0) {
1365 fprintf(stderr,".");
1366 }
1367 #endif
1368 }
1369 *npages = nzero + npos;
1370 *seconds = Stopwatch_stop(stopwatch);
1371 Stopwatch_free(&stopwatch);
1372 }
1373 }
1374
1375 return memory;
1376 }
1377 #endif
1378
1379
1380