Move fileio sources to C++
[alexxy/gromacs.git] / src / gromacs / fileio / gmxfio.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "gmxfio.h"
40
41 #include "config.h"
42
43 #include <cerrno>
44 #include <cstdio>
45 #include <cstring>
46
47 #ifdef HAVE_IO_H
48 #include <io.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53
54 #include "thread_mpi/threads.h"
55
56 #include "gromacs/fileio/filenm.h"
57 #include "gromacs/fileio/md5.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/futil.h"
60 #include "gromacs/utility/smalloc.h"
61
62 #include "gmxfio-impl.h"
63
64 /* This is the new improved and thread safe version of gmxfio. */
65
66
67
68 /* the list of open files is a linked list, with a dummy element at its head;
69        it is initialized when the first file is opened. */
70 static t_fileio *open_files = NULL;
71
72
73 /* this mutex locks the open_files structure so that no two threads can
74    modify it.
75
76    For now, we use this as a coarse grained lock on all file
77    insertion/deletion operations because it makes avoiding deadlocks
78    easier, and adds almost no overhead: the only overhead is during
79    opening and closing of files, or during global operations like
80    iterating along all open files. All these cases should be rare
81    during the simulation. */
82 static tMPI_Thread_mutex_t open_file_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
83
84 /******************************************************************
85  *
86  * Internal functions:
87  *
88  ******************************************************************/
89
90 static int gmx_fio_int_flush(t_fileio* fio)
91 {
92     int rc = 0;
93
94     if (fio->fp)
95     {
96         rc = fflush(fio->fp);
97     }
98
99     return rc;
100 }
101
102 /* lock the mutex associated with this fio. This needs to be done for every
103    type of access to the fio's elements. */
104 void gmx_fio_lock(t_fileio *fio)
105 {
106     tMPI_Lock_lock(&(fio->mtx));
107 }
108 /* unlock the mutex associated with this fio.  */
109 void gmx_fio_unlock(t_fileio *fio)
110 {
111     tMPI_Lock_unlock(&(fio->mtx));
112 }
113
114 /* make a dummy head element, assuming we locked everything. */
115 static void gmx_fio_make_dummy(void)
116 {
117     if (!open_files)
118     {
119         snew(open_files, 1);
120         open_files->fp   = NULL;
121         open_files->fn   = NULL;
122         open_files->next = open_files;
123         open_files->prev = open_files;
124         tMPI_Lock_init(&(open_files->mtx));
125     }
126 }
127
128
129
130
131
132
133
134 /***********************************************************************
135  *
136  * FILE LIST OPERATIONS
137  *
138  ***********************************************************************/
139
140
141 /* insert a new t_fileio into the list */
142 static void gmx_fio_insert(t_fileio *fio)
143 {
144     t_fileio *prev;
145     /* first lock the big open_files mutex. */
146     tMPI_Thread_mutex_lock(&open_file_mutex);
147     /* now check whether the dummy element has been allocated,
148        and allocate it if it hasn't */
149     gmx_fio_make_dummy();
150
151     /* and lock the fio we got and the list's head **/
152     gmx_fio_lock(fio);
153     gmx_fio_lock(open_files);
154     prev = open_files->prev;
155     /* lock the element after the current one */
156     if (prev != open_files)
157     {
158         gmx_fio_lock(prev);
159     }
160
161     /* now do the actual insertion: */
162     fio->next        = open_files;
163     open_files->prev = fio;
164     prev->next       = fio;
165     fio->prev        = prev;
166
167     /* now unlock all our locks */
168     if (prev != open_files)
169     {
170         gmx_fio_unlock(prev);
171     }
172     gmx_fio_unlock(open_files);
173     gmx_fio_unlock(fio);
174
175     /* now unlock the big open_files mutex.  */
176     tMPI_Thread_mutex_unlock(&open_file_mutex);
177 }
178
179 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
180    it locked.
181    NOTE: We also assume that the open_file_mutex has been locked */
182 static void gmx_fio_remove(t_fileio *fio)
183 {
184     /* lock prev, because we're changing it */
185     gmx_fio_lock(fio->prev);
186
187     /* now set the prev's pointer */
188     fio->prev->next = fio->next;
189     gmx_fio_unlock(fio->prev);
190
191     /* with the next ptr, we can simply lock while the original was locked */
192     gmx_fio_lock(fio->next);
193     fio->next->prev = fio->prev;
194     gmx_fio_unlock(fio->next);
195
196     /* and make sure we point nowhere in particular */
197     fio->next = fio->prev = fio;
198 }
199
200
201 /* get the first open file, or NULL if there is none.
202    Returns a locked fio. */
203 static t_fileio *gmx_fio_get_first(void)
204 {
205     t_fileio *ret;
206     /* first lock the big open_files mutex and the dummy's mutex */
207
208     /* first lock the big open_files mutex. */
209     tMPI_Thread_mutex_lock(&open_file_mutex);
210     gmx_fio_make_dummy();
211
212     gmx_fio_lock(open_files);
213     ret = open_files->next;
214
215
216     /* check whether there were any to begin with */
217     if (ret == open_files)
218     {
219         /* after this, the open_file pointer should never change */
220         ret = NULL;
221     }
222     else
223     {
224         gmx_fio_lock(open_files->next);
225     }
226     gmx_fio_unlock(open_files);
227
228
229     return ret;
230 }
231
232 /* get the next open file, or NULL if there is none.
233    Unlocks the previous fio and locks the next one. */
234 static t_fileio *gmx_fio_get_next(t_fileio *fio)
235 {
236     t_fileio *ret;
237
238     ret = fio->next;
239     /* check if that was the last one */
240     if (fio->next == open_files)
241     {
242         ret = NULL;
243         tMPI_Thread_mutex_unlock(&open_file_mutex);
244     }
245     else
246     {
247         gmx_fio_lock(ret);
248     }
249     gmx_fio_unlock(fio);
250
251     return ret;
252 }
253
254 /* Stop looping through the open_files.  Unlocks the global lock. */
255 static void gmx_fio_stop_getting_next(t_fileio *fio)
256 {
257     gmx_fio_unlock(fio);
258     tMPI_Thread_mutex_unlock(&open_file_mutex);
259 }
260
261
262
263
264 /*****************************************************************
265  *
266  *                     EXPORTED SECTION
267  *
268  *****************************************************************/
269 t_fileio *gmx_fio_open(const char *fn, const char *mode)
270 {
271     t_fileio *fio = NULL;
272     char      newmode[5];
273     gmx_bool  bRead, bReadWrite;
274
275     /* sanitize the mode string */
276     if (std::strncmp(mode, "r+", 2) == 0)
277     {
278         std::strcpy(newmode, "r+");
279     }
280     else if (mode[0] == 'r')
281     {
282         std::strcpy(newmode, "r");
283     }
284     else if (strncmp(mode, "w+", 2) == 0)
285     {
286         std::strcpy(newmode, "w+");
287     }
288     else if (mode[0] == 'w')
289     {
290         std::strcpy(newmode, "w");
291     }
292     else if (strncmp(mode, "a+", 2) == 0)
293     {
294         std::strcpy(newmode, "a+");
295     }
296     else if (mode[0] == 'a')
297     {
298         std::strcpy(newmode, "a");
299     }
300     else
301     {
302         gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
303     }
304
305     /* Check if it should be opened as a binary file */
306     if (!ftp_is_text(fn2ftp(fn)))
307     {
308         strcat(newmode, "b");
309     }
310
311     snew(fio, 1);
312     tMPI_Lock_init(&(fio->mtx));
313     bRead      = (newmode[0] == 'r' && newmode[1] != '+');
314     bReadWrite = (newmode[1] == '+');
315     fio->fp    = NULL;
316     fio->xdr   = NULL;
317     if (fn)
318     {
319         if (fn2ftp(fn) == efTNG)
320         {
321             gmx_incons("gmx_fio_open may not be used to open TNG files");
322         }
323         fio->iFTP   = fn2ftp(fn);
324         fio->fn     = gmx_strdup(fn);
325
326         fio->fp = gmx_ffopen(fn, newmode);
327         /* If this file type is in the list of XDR files, open it like that */
328         if (ftp_is_xdr(fio->iFTP))
329         {
330             /* determine the XDR direction */
331             if (newmode[0] == 'w' || newmode[0] == 'a')
332             {
333                 fio->xdrmode = XDR_ENCODE;
334             }
335             else
336             {
337                 fio->xdrmode = XDR_DECODE;
338             }
339             snew(fio->xdr, 1);
340             xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
341         }
342
343         /* for appending seek to end of file to make sure ftell gives correct position
344          * important for checkpointing */
345         if (newmode[0] == 'a')
346         {
347             gmx_fseek(fio->fp, 0, SEEK_END);
348         }
349     }
350     else
351     {
352         gmx_fatal(FARGS, "Cannot open file with NULL filename string");
353     }
354
355     fio->bRead             = bRead;
356     fio->bReadWrite        = bReadWrite;
357     fio->bDouble           = (sizeof(real) == sizeof(double));
358
359     /* and now insert this file into the list of open files. */
360     gmx_fio_insert(fio);
361     return fio;
362 }
363
364 static int gmx_fio_close_locked(t_fileio *fio)
365 {
366     int rc = 0;
367
368     if (fio->xdr != NULL)
369     {
370         xdr_destroy(fio->xdr);
371         sfree(fio->xdr);
372     }
373
374     if (fio->fp != NULL)
375     {
376         rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
377
378     }
379
380     return rc;
381 }
382
383 int gmx_fio_close(t_fileio *fio)
384 {
385     int rc = 0;
386
387     /* first lock the big open_files mutex. */
388     /* We don't want two processes operating on the list at the same time */
389     tMPI_Thread_mutex_lock(&open_file_mutex);
390
391     gmx_fio_lock(fio);
392     /* first remove it from the list */
393     gmx_fio_remove(fio);
394     rc = gmx_fio_close_locked(fio);
395     gmx_fio_unlock(fio);
396
397     sfree(fio->fn);
398     sfree(fio);
399
400     tMPI_Thread_mutex_unlock(&open_file_mutex);
401
402     return rc;
403 }
404
405 /* close only fp but keep FIO entry. */
406 int gmx_fio_fp_close(t_fileio *fio)
407 {
408     int rc = 0;
409     gmx_fio_lock(fio);
410     if (fio->xdr == NULL)
411     {
412         rc      = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
413         fio->fp = NULL;
414     }
415     gmx_fio_unlock(fio);
416
417     return rc;
418 }
419
420 FILE * gmx_fio_fopen(const char *fn, const char *mode)
421 {
422     FILE     *ret;
423     t_fileio *fio;
424
425     fio = gmx_fio_open(fn, mode);
426     gmx_fio_lock(fio);
427     ret = fio->fp;
428     gmx_fio_unlock(fio);
429
430     return ret;
431 }
432
433 int gmx_fio_fclose(FILE *fp)
434 {
435     t_fileio *cur;
436     int       rc    = -1;
437
438     cur = gmx_fio_get_first();
439     while (cur)
440     {
441         if (cur->fp == fp)
442         {
443             rc = gmx_fio_close_locked(cur);
444             gmx_fio_remove(cur);
445             gmx_fio_stop_getting_next(cur);
446             sfree(cur->fn);
447             sfree(cur);
448             break;
449         }
450         cur = gmx_fio_get_next(cur);
451     }
452
453     return rc;
454 }
455
456 /* internal variant of get_file_md5 that operates on a locked file */
457 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
458                                     unsigned char digest[])
459 {
460     /*1MB: large size important to catch almost identical files */
461 #define CPT_CHK_LEN  1048576
462     md5_state_t    state;
463     unsigned char *buf;
464     gmx_off_t      read_len;
465     gmx_off_t      seek_offset;
466     int            ret = -1;
467
468     seek_offset = offset - CPT_CHK_LEN;
469     if (seek_offset < 0)
470     {
471         seek_offset = 0;
472     }
473     read_len = offset - seek_offset;
474
475
476     if (fio->fp && fio->bReadWrite)
477     {
478         ret = gmx_fseek(fio->fp, seek_offset, SEEK_SET);
479         if (ret)
480         {
481             gmx_fseek(fio->fp, 0, SEEK_END);
482         }
483     }
484     if (ret) /*either no fp, not readwrite, or fseek not successful */
485     {
486         return -1;
487     }
488
489     snew(buf, CPT_CHK_LEN);
490     /* the read puts the file position back to offset */
491     if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
492     {
493         /* not fatal: md5sum check to prevent overwriting files
494          * works (less safe) without
495          * */
496         if (ferror(fio->fp))
497         {
498             fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
499                     strerror(errno));
500         }
501         else if (feof(fio->fp))
502         {
503             /*
504              * For long runs that checkpoint frequently but write e.g. logs
505              * infrequently we don't want to issue lots of warnings before we
506              * have written anything to the log.
507              */
508             if (0)
509             {
510                 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
511             }
512         }
513         else
514         {
515             fprintf(
516                     stderr,
517                     "\nTrying to get md5sum: Unknown reason for short read: %s\n",
518                     fio->fn);
519         }
520
521         gmx_fseek(fio->fp, 0, SEEK_END);
522
523         ret = -1;
524     }
525     gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
526                                         it gives problems otherwise*/
527
528     if (debug)
529     {
530         fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
531     }
532
533     if (!ret)
534     {
535         gmx_md5_init(&state);
536         gmx_md5_append(&state, buf, read_len);
537         gmx_md5_finish(&state, digest);
538         ret = read_len;
539     }
540     sfree(buf);
541     return ret;
542 }
543
544
545 /*
546  * fio: file to compute md5 for
547  * offset: starting pointer of region to use for md5
548  * digest: return array of md5 sum
549  */
550 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
551                          unsigned char digest[])
552 {
553     int ret;
554
555     gmx_fio_lock(fio);
556     ret = gmx_fio_int_get_file_md5(fio, offset, digest);
557     gmx_fio_unlock(fio);
558
559     return ret;
560 }
561
562 /* The fio_mutex should ALWAYS be locked when this function is called */
563 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
564 {
565     /* Flush the file, so we are sure it is written */
566     if (gmx_fio_int_flush(fio))
567     {
568         char buf[STRLEN];
569         sprintf(
570                 buf,
571                 "Cannot write file '%s'; maybe you are out of disk space?",
572                 fio->fn);
573         gmx_file(buf);
574     }
575
576     /* We cannot count on XDR being able to write 64-bit integers,
577        so separate into high/low 32-bit values.
578        In case the filesystem has 128-bit offsets we only care
579        about the first 64 bits - we'll have to fix
580        this when exabyte-size output files are common...
581      */
582     *offset = gmx_ftell(fio->fp);
583
584     return 0;
585 }
586
587 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
588                                       int                  *p_nfiles)
589 {
590     int                   nfiles, nalloc;
591     gmx_file_position_t * outputfiles;
592     t_fileio             *cur;
593
594     nfiles = 0;
595
596     /* pre-allocate 100 files */
597     nalloc = 100;
598     snew(outputfiles, nalloc);
599
600     cur = gmx_fio_get_first();
601     while (cur)
602     {
603         /* Skip the checkpoint files themselves, since they could be open when
604            we call this routine... */
605         if (!cur->bRead && cur->iFTP != efCPT)
606         {
607             /* This is an output file currently open for writing, add it */
608             if (nfiles == nalloc)
609             {
610                 nalloc += 100;
611                 srenew(outputfiles, nalloc);
612             }
613
614             std::strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
615
616             /* Get the file position */
617             gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
618 #ifndef GMX_FAHCORE
619             outputfiles[nfiles].chksum_size
620                 = gmx_fio_int_get_file_md5(cur,
621                                            outputfiles[nfiles].offset,
622                                            outputfiles[nfiles].chksum);
623 #endif
624             nfiles++;
625         }
626
627         cur = gmx_fio_get_next(cur);
628     }
629     *p_nfiles      = nfiles;
630     *p_outputfiles = outputfiles;
631
632     return 0;
633 }
634
635
636 char *gmx_fio_getname(t_fileio *fio)
637 {
638     char *ret;
639     gmx_fio_lock(fio);
640     ret = fio->fn;
641     gmx_fio_unlock(fio);
642
643     return ret;
644 }
645
646 int gmx_fio_getftp(t_fileio* fio)
647 {
648     int ret;
649
650     gmx_fio_lock(fio);
651     ret = fio->iFTP;
652     gmx_fio_unlock(fio);
653
654     return ret;
655 }
656
657 void gmx_fio_rewind(t_fileio* fio)
658 {
659     gmx_fio_lock(fio);
660
661     if (fio->xdr)
662     {
663         xdr_destroy(fio->xdr);
664         frewind(fio->fp);
665         xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
666     }
667     else
668     {
669         frewind(fio->fp);
670     }
671     gmx_fio_unlock(fio);
672 }
673
674
675 int gmx_fio_flush(t_fileio* fio)
676 {
677     int ret;
678
679     gmx_fio_lock(fio);
680     ret = gmx_fio_int_flush(fio);
681     gmx_fio_unlock(fio);
682
683     return ret;
684 }
685
686
687
688 static int gmx_fio_int_fsync(t_fileio *fio)
689 {
690     int rc    = 0;
691
692     if (fio->fp)
693     {
694         rc = gmx_fsync(fio->fp);
695     }
696     return rc;
697 }
698
699
700 int gmx_fio_fsync(t_fileio *fio)
701 {
702     int rc;
703
704     gmx_fio_lock(fio);
705     rc = gmx_fio_int_fsync(fio);
706     gmx_fio_unlock(fio);
707
708     return rc;
709 }
710
711
712
713 t_fileio *gmx_fio_all_output_fsync(void)
714 {
715     t_fileio *ret = NULL;
716     t_fileio *cur;
717
718     cur = gmx_fio_get_first();
719     while (cur)
720     {
721         if (!cur->bRead)
722         {
723             /* if any of them fails, return failure code */
724             int rc = gmx_fio_int_fsync(cur);
725             if (rc != 0 && !ret)
726             {
727                 ret = cur;
728             }
729         }
730         cur = gmx_fio_get_next(cur);
731     }
732
733     /* in addition, we force these to be written out too, if they're being
734        redirected. We don't check for errors because errors most likely mean
735        that they're not redirected. */
736     fflush(stdout);
737     fflush(stderr);
738 #if (defined(HAVE_FSYNC))
739     /* again, fahcore defines HAVE_FSYNC and fsync() */
740     fsync(STDOUT_FILENO);
741     fsync(STDERR_FILENO);
742 #endif
743
744     return ret;
745 }
746
747
748 gmx_off_t gmx_fio_ftell(t_fileio* fio)
749 {
750     gmx_off_t ret = 0;
751
752     gmx_fio_lock(fio);
753     if (fio->fp)
754     {
755         ret = gmx_ftell(fio->fp);
756     }
757     gmx_fio_unlock(fio);
758     return ret;
759 }
760
761 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
762 {
763     int rc;
764
765     gmx_fio_lock(fio);
766     if (fio->fp)
767     {
768         rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
769     }
770     else
771     {
772         gmx_file(fio->fn);
773         rc = -1;
774     }
775     gmx_fio_unlock(fio);
776     return rc;
777 }
778
779 FILE *gmx_fio_getfp(t_fileio *fio)
780 {
781     FILE *ret = NULL;
782
783     gmx_fio_lock(fio);
784     if (fio->fp)
785     {
786         ret = fio->fp;
787     }
788     gmx_fio_unlock(fio);
789     return ret;
790 }
791
792 gmx_bool gmx_fio_getread(t_fileio* fio)
793 {
794     gmx_bool ret;
795
796     gmx_fio_lock(fio);
797     ret = fio->bRead;
798     gmx_fio_unlock(fio);
799
800     return ret;
801 }
802
803 int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
804 {
805     int ret;
806
807     gmx_fio_lock(fio);
808     ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
809     gmx_fio_unlock(fio);
810
811     return ret;
812 }