ab2b1832962759a887b0e5d5d71df7036a976ec9
[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,2016,2017,2018,2019, 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 #include <vector>
48
49 #if HAVE_IO_H
50 #    include <io.h>
51 #endif
52 #ifdef HAVE_UNISTD_H
53 #    include <unistd.h>
54 #endif
55
56 #include "thread_mpi/threads.h"
57
58 #include "gromacs/fileio/filetypes.h"
59 #include "gromacs/fileio/md5.h"
60 #include "gromacs/utility/fatalerror.h"
61 #include "gromacs/utility/futil.h"
62 #include "gromacs/utility/mutex.h"
63 #include "gromacs/utility/smalloc.h"
64
65 #include "gmxfio_impl.h"
66
67 /* This is the new improved and thread safe version of gmxfio. */
68
69
70 /* the list of open files is a linked list, with a dummy element at its head;
71        it is initialized when the first file is opened. */
72 static t_fileio* open_files = nullptr;
73
74
75 /* this mutex locks the open_files structure so that no two threads can
76    modify it.
77
78    For now, we use this as a coarse grained lock on all file
79    insertion/deletion operations because it makes avoiding deadlocks
80    easier, and adds almost no overhead: the only overhead is during
81    opening and closing of files, or during global operations like
82    iterating along all open files. All these cases should be rare
83    during the simulation. */
84 static gmx::Mutex open_file_mutex;
85
86 using Lock = gmx::lock_guard<gmx::Mutex>;
87
88 /******************************************************************
89  *
90  * Internal functions:
91  *
92  ******************************************************************/
93
94 static int gmx_fio_int_flush(t_fileio* fio)
95 {
96     int rc = 0;
97
98     if (fio->fp)
99     {
100         rc = fflush(fio->fp);
101     }
102
103     return rc;
104 }
105
106 /* lock the mutex associated with this fio. This needs to be done for every
107    type of access to the fio's elements. */
108 void gmx_fio_lock(t_fileio* fio)
109 {
110     tMPI_Lock_lock(&(fio->mtx));
111 }
112 /* unlock the mutex associated with this fio.  */
113 void gmx_fio_unlock(t_fileio* fio)
114 {
115     tMPI_Lock_unlock(&(fio->mtx));
116 }
117
118 /* make a dummy head element, assuming we locked everything. */
119 static void gmx_fio_make_dummy()
120 {
121     if (!open_files)
122     {
123         open_files       = new t_fileio{};
124         open_files->fp   = nullptr;
125         open_files->fn   = nullptr;
126         open_files->next = open_files;
127         open_files->prev = open_files;
128         tMPI_Lock_init(&(open_files->mtx));
129     }
130 }
131
132
133 /***********************************************************************
134  *
135  * FILE LIST OPERATIONS
136  *
137  ***********************************************************************/
138
139
140 /* insert a new t_fileio into the list */
141 static void gmx_fio_insert(t_fileio* fio)
142 {
143     t_fileio* prev;
144     Lock      openFilesLock(open_file_mutex);
145     gmx_fio_make_dummy();
146
147     /* and lock the fio we got and the list's head **/
148     gmx_fio_lock(fio);
149     gmx_fio_lock(open_files);
150     prev = open_files->prev;
151     /* lock the element after the current one */
152     if (prev != open_files)
153     {
154         gmx_fio_lock(prev);
155     }
156
157     /* now do the actual insertion: */
158     fio->next        = open_files;
159     open_files->prev = fio;
160     prev->next       = fio;
161     fio->prev        = prev;
162
163     /* now unlock all our locks */
164     if (prev != open_files)
165     {
166         gmx_fio_unlock(prev);
167     }
168     gmx_fio_unlock(open_files);
169     gmx_fio_unlock(fio);
170 }
171
172 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
173    it locked.
174    NOTE: We also assume that the open_file_mutex has been locked */
175 static void gmx_fio_remove(t_fileio* fio)
176 {
177     /* lock prev, because we're changing it */
178     gmx_fio_lock(fio->prev);
179
180     /* now set the prev's pointer */
181     fio->prev->next = fio->next;
182     gmx_fio_unlock(fio->prev);
183
184     /* with the next ptr, we can simply lock while the original was locked */
185     gmx_fio_lock(fio->next);
186     fio->next->prev = fio->prev;
187     gmx_fio_unlock(fio->next);
188
189     /* and make sure we point nowhere in particular */
190     fio->next = fio->prev = fio;
191 }
192
193
194 /* get the first open file, or NULL if there is none.
195    Returns a locked fio. Assumes open_files_mutex is locked. */
196 static t_fileio* gmx_fio_get_first()
197 {
198     t_fileio* ret;
199
200     gmx_fio_make_dummy();
201
202     gmx_fio_lock(open_files);
203     ret = open_files->next;
204
205
206     /* check whether there were any to begin with */
207     if (ret == open_files)
208     {
209         /* after this, the open_file pointer should never change */
210         ret = nullptr;
211     }
212     else
213     {
214         gmx_fio_lock(open_files->next);
215     }
216     gmx_fio_unlock(open_files);
217
218
219     return ret;
220 }
221
222 /* get the next open file, or NULL if there is none.
223    Unlocks the previous fio and locks the next one.
224    Assumes open_file_mutex is locked. */
225 static t_fileio* gmx_fio_get_next(t_fileio* fio)
226 {
227     t_fileio* ret;
228
229     ret = fio->next;
230     /* check if that was the last one */
231     if (fio->next == open_files)
232     {
233         ret = nullptr;
234     }
235     else
236     {
237         gmx_fio_lock(ret);
238     }
239     gmx_fio_unlock(fio);
240
241     return ret;
242 }
243
244 /* Stop looping through the open_files. Assumes open_file_mutex is locked. */
245 static void gmx_fio_stop_getting_next(t_fileio* fio)
246 {
247     gmx_fio_unlock(fio);
248 }
249
250
251 /*****************************************************************
252  *
253  *                     EXPORTED SECTION
254  *
255  *****************************************************************/
256 t_fileio* gmx_fio_open(const char* fn, const char* mode)
257 {
258     t_fileio* fio = nullptr;
259     char      newmode[5];
260     gmx_bool  bRead, bReadWrite;
261
262     /* sanitize the mode string */
263     if (std::strncmp(mode, "r+", 2) == 0)
264     {
265         std::strcpy(newmode, "r+");
266     }
267     else if (mode[0] == 'r')
268     {
269         std::strcpy(newmode, "r");
270     }
271     else if (strncmp(mode, "w+", 2) == 0)
272     {
273         std::strcpy(newmode, "w+");
274     }
275     else if (mode[0] == 'w')
276     {
277         std::strcpy(newmode, "w");
278     }
279     else if (strncmp(mode, "a+", 2) == 0)
280     {
281         std::strcpy(newmode, "a+");
282     }
283     else if (mode[0] == 'a')
284     {
285         std::strcpy(newmode, "a");
286     }
287     else
288     {
289         gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
290     }
291
292     /* Check if it should be opened as a binary file */
293     if (!ftp_is_text(fn2ftp(fn)))
294     {
295         strcat(newmode, "b");
296     }
297
298     fio = new t_fileio{};
299     tMPI_Lock_init(&(fio->mtx));
300     bRead      = (newmode[0] == 'r' && newmode[1] != '+');
301     bReadWrite = (newmode[1] == '+');
302     fio->fp    = nullptr;
303     fio->xdr   = nullptr;
304     if (fn)
305     {
306         if (fn2ftp(fn) == efTNG)
307         {
308             gmx_incons("gmx_fio_open may not be used to open TNG files");
309         }
310         fio->iFTP = fn2ftp(fn);
311         fio->fn   = gmx_strdup(fn);
312
313         fio->fp = gmx_ffopen(fn, newmode);
314         /* If this file type is in the list of XDR files, open it like that */
315         if (ftp_is_xdr(fio->iFTP))
316         {
317             /* determine the XDR direction */
318             if (newmode[0] == 'w' || newmode[0] == 'a')
319             {
320                 fio->xdrmode = XDR_ENCODE;
321             }
322             else
323             {
324                 fio->xdrmode = XDR_DECODE;
325             }
326             snew(fio->xdr, 1);
327             xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
328         }
329
330         /* for appending seek to end of file to make sure ftell gives correct position
331          * important for checkpointing */
332         if (newmode[0] == 'a')
333         {
334             gmx_fseek(fio->fp, 0, SEEK_END);
335         }
336     }
337     else
338     {
339         gmx_fatal(FARGS, "Cannot open file with NULL filename string");
340     }
341
342     fio->bRead      = bRead;
343     fio->bReadWrite = bReadWrite;
344     fio->bDouble    = (sizeof(real) == sizeof(double));
345
346     /* and now insert this file into the list of open files. */
347     gmx_fio_insert(fio);
348     return fio;
349 }
350
351 static int gmx_fio_close_locked(t_fileio* fio)
352 {
353     int rc = 0;
354
355     if (fio->xdr != nullptr)
356     {
357         xdr_destroy(fio->xdr);
358         sfree(fio->xdr);
359     }
360
361     if (fio->fp != nullptr)
362     {
363         rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
364     }
365
366     return rc;
367 }
368
369 int gmx_fio_close(t_fileio* fio)
370 {
371     int rc = 0;
372
373     Lock openFilesLock(open_file_mutex);
374
375     gmx_fio_lock(fio);
376     /* first remove it from the list */
377     gmx_fio_remove(fio);
378     rc = gmx_fio_close_locked(fio);
379     gmx_fio_unlock(fio);
380
381     sfree(fio->fn);
382     delete fio;
383
384     return rc;
385 }
386
387 /* close only fp but keep FIO entry. */
388 int gmx_fio_fp_close(t_fileio* fio)
389 {
390     int rc = 0;
391     gmx_fio_lock(fio);
392     if (fio->xdr == nullptr)
393     {
394         rc      = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
395         fio->fp = nullptr;
396     }
397     gmx_fio_unlock(fio);
398
399     return rc;
400 }
401
402 FILE* gmx_fio_fopen(const char* fn, const char* mode)
403 {
404     FILE*     ret;
405     t_fileio* fio;
406
407     fio = gmx_fio_open(fn, mode);
408     gmx_fio_lock(fio);
409     ret = fio->fp;
410     gmx_fio_unlock(fio);
411
412     return ret;
413 }
414
415 int gmx_fio_fclose(FILE* fp)
416 {
417     t_fileio* cur;
418     int       rc = -1;
419
420     Lock openFilesLock(open_file_mutex);
421     cur = gmx_fio_get_first();
422     while (cur)
423     {
424         if (cur->fp == fp)
425         {
426             rc = gmx_fio_close_locked(cur);
427             gmx_fio_remove(cur);
428             gmx_fio_stop_getting_next(cur);
429             sfree(cur->fn);
430             delete cur;
431             break;
432         }
433         cur = gmx_fio_get_next(cur);
434     }
435
436     return rc;
437 }
438
439 //! Helper struct for returning the MD5 checksum and the amount of the file that contributed to it.
440 struct MD5Checksum
441 {
442     //! Checksum md5 digest.
443     std::array<unsigned char, 16> checksum;
444     //! The length of the file that contributed to the digest.
445     gmx_off_t readLength;
446 };
447
448 /*! \brief Internal variant of get_file_md5 that operates on a locked
449  * file.
450  *
451  * \return -1 any time a checksum cannot be computed, otherwise the
452  *            length of the data from which the checksum was computed. */
453 static int gmx_fio_int_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum)
454 {
455     /*1MB: large size important to catch almost identical files */
456     constexpr size_t maximumChecksumInputSize = 1048576;
457     md5_state_t      state;
458     gmx_off_t        readLength;
459     gmx_off_t        seekOffset;
460
461     seekOffset = offset - maximumChecksumInputSize;
462     if (seekOffset < 0)
463     {
464         seekOffset = 0;
465     }
466     readLength = offset - seekOffset;
467
468     if (!fio->fp)
469     {
470         // It's not an error if the file isn't open.
471         return -1;
472     }
473     if (!fio->bReadWrite)
474     {
475         // It's not an error if the file is open in the wrong mode.
476         //
477         // TODO It is unclear why this check exists. The bReadWrite
478         // flag is true when the file-opening mode included "+" but we
479         // only need read and seek to be able to compute the
480         // md5sum. Other requirements (e.g. that we can truncate when
481         // doing an appending restart) should be expressed in a
482         // different way, but it is unclear whether that is part of
483         // the logic here.
484         return -1;
485     }
486
487     if (gmx_fseek(fio->fp, seekOffset, SEEK_SET))
488     {
489         // It's not an error if file seeking fails. (But it could be
490         // an issue when moving a checkpoint from one platform to
491         // another, when they differ in their support for seeking, and
492         // so can't agree on a checksum for appending).
493         gmx_fseek(fio->fp, 0, SEEK_END);
494         return -1;
495     }
496
497     std::vector<unsigned char> buf(maximumChecksumInputSize);
498     // The fread puts the file position back to offset.
499     if (static_cast<gmx_off_t>(fread(buf.data(), 1, readLength, fio->fp)) != readLength)
500     {
501         // Read an unexpected length. This is not a fatal error; the
502         // md5sum check to prevent overwriting files is not vital.
503         if (ferror(fio->fp))
504         {
505             fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn, strerror(errno));
506         }
507         else if (!feof(fio->fp))
508         {
509             fprintf(stderr, "\nTrying to get md5sum: Unknown reason for short read: %s\n", fio->fn);
510         }
511
512         gmx_fseek(fio->fp, 0, SEEK_END);
513         return -1;
514     }
515     // Return the file position to the end of the file.
516     gmx_fseek(fio->fp, 0, SEEK_END);
517
518     if (debug)
519     {
520         fprintf(debug, "chksum %s readlen %ld\n", fio->fn, static_cast<long int>(readLength));
521     }
522
523     gmx_md5_init(&state);
524     gmx_md5_append(&state, buf.data(), readLength);
525     *checksum = gmx_md5_finish(&state);
526     return readLength;
527 }
528
529
530 /*
531  * fio: file to compute md5 for
532  * offset: starting pointer of region to use for md5
533  * digest: return array of md5 sum
534  */
535 int gmx_fio_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum)
536 {
537     int ret;
538
539     gmx_fio_lock(fio);
540     ret = gmx_fio_int_get_file_md5(fio, offset, checksum);
541     gmx_fio_unlock(fio);
542
543     return ret;
544 }
545
546 /* The fio_mutex should ALWAYS be locked when this function is called */
547 static int gmx_fio_int_get_file_position(t_fileio* fio, gmx_off_t* offset)
548 {
549     /* Flush the file, so we are sure it is written */
550     if (gmx_fio_int_flush(fio))
551     {
552         char buf[STRLEN];
553         sprintf(buf, "Cannot write file '%s'; maybe you are out of disk space?", fio->fn);
554         gmx_file(buf);
555     }
556
557     /* We cannot count on XDR being able to write 64-bit integers,
558        so separate into high/low 32-bit values.
559        In case the filesystem has 128-bit offsets we only care
560        about the first 64 bits - we'll have to fix
561        this when exabyte-size output files are common...
562      */
563     *offset = gmx_ftell(fio->fp);
564
565     return 0;
566 }
567
568 std::vector<gmx_file_position_t> gmx_fio_get_output_file_positions()
569 {
570     std::vector<gmx_file_position_t> outputfiles;
571     t_fileio*                        cur;
572
573     Lock openFilesLock(open_file_mutex);
574     cur = gmx_fio_get_first();
575     while (cur)
576     {
577         /* Skip the checkpoint files themselves, since they could be open when
578            we call this routine... */
579         if (!cur->bRead && cur->iFTP != efCPT)
580         {
581             outputfiles.emplace_back();
582
583             std::strncpy(outputfiles.back().filename, cur->fn, STRLEN - 1);
584
585             /* Get the file position */
586             gmx_fio_int_get_file_position(cur, &outputfiles.back().offset);
587             if (!GMX_FAHCORE)
588             {
589                 outputfiles.back().checksumSize = gmx_fio_int_get_file_md5(
590                         cur, outputfiles.back().offset, &outputfiles.back().checksum);
591             }
592         }
593
594         cur = gmx_fio_get_next(cur);
595     }
596
597     return outputfiles;
598 }
599
600
601 char* gmx_fio_getname(t_fileio* fio)
602 {
603     char* ret;
604     gmx_fio_lock(fio);
605     ret = fio->fn;
606     gmx_fio_unlock(fio);
607
608     return ret;
609 }
610
611 int gmx_fio_getftp(t_fileio* fio)
612 {
613     int ret;
614
615     gmx_fio_lock(fio);
616     ret = fio->iFTP;
617     gmx_fio_unlock(fio);
618
619     return ret;
620 }
621
622 void gmx_fio_rewind(t_fileio* fio)
623 {
624     gmx_fio_lock(fio);
625
626     if (fio->xdr)
627     {
628         xdr_destroy(fio->xdr);
629         frewind(fio->fp);
630         xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
631     }
632     else
633     {
634         frewind(fio->fp);
635     }
636     gmx_fio_unlock(fio);
637 }
638
639
640 int gmx_fio_flush(t_fileio* fio)
641 {
642     int ret;
643
644     gmx_fio_lock(fio);
645     ret = gmx_fio_int_flush(fio);
646     gmx_fio_unlock(fio);
647
648     return ret;
649 }
650
651
652 static int gmx_fio_int_fsync(t_fileio* fio)
653 {
654     int rc = 0;
655
656     if (fio->fp)
657     {
658         rc = gmx_fsync(fio->fp);
659     }
660     return rc;
661 }
662
663
664 int gmx_fio_fsync(t_fileio* fio)
665 {
666     int rc;
667
668     gmx_fio_lock(fio);
669     rc = gmx_fio_int_fsync(fio);
670     gmx_fio_unlock(fio);
671
672     return rc;
673 }
674
675
676 t_fileio* gmx_fio_all_output_fsync()
677 {
678     t_fileio* ret = nullptr;
679     t_fileio* cur;
680
681     Lock openFilesLock(open_file_mutex);
682     cur = gmx_fio_get_first();
683     while (cur)
684     {
685         if (!cur->bRead)
686         {
687             /* if any of them fails, return failure code */
688             int rc = gmx_fio_int_fsync(cur);
689             if (rc != 0 && !ret)
690             {
691                 ret = cur;
692             }
693         }
694         cur = gmx_fio_get_next(cur);
695     }
696
697     /* in addition, we force these to be written out too, if they're being
698        redirected. We don't check for errors because errors most likely mean
699        that they're not redirected. */
700     fflush(stdout);
701     fflush(stderr);
702 #if HAVE_FSYNC
703     /* again, fahcore defines HAVE_FSYNC and fsync() */
704     fsync(STDOUT_FILENO);
705     fsync(STDERR_FILENO);
706 #endif
707
708     return ret;
709 }
710
711
712 gmx_off_t gmx_fio_ftell(t_fileio* fio)
713 {
714     gmx_off_t ret = 0;
715
716     gmx_fio_lock(fio);
717     if (fio->fp)
718     {
719         ret = gmx_ftell(fio->fp);
720     }
721     gmx_fio_unlock(fio);
722     return ret;
723 }
724
725 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
726 {
727     int rc;
728
729     gmx_fio_lock(fio);
730     if (fio->fp)
731     {
732         rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
733     }
734     else
735     {
736         gmx_file(fio->fn);
737     }
738     gmx_fio_unlock(fio);
739     return rc;
740 }
741
742 FILE* gmx_fio_getfp(t_fileio* fio)
743 {
744     FILE* ret = nullptr;
745
746     gmx_fio_lock(fio);
747     if (fio->fp)
748     {
749         ret = fio->fp;
750     }
751     gmx_fio_unlock(fio);
752     return ret;
753 }
754
755 gmx_bool gmx_fio_getread(t_fileio* fio)
756 {
757     gmx_bool ret;
758
759     gmx_fio_lock(fio);
760     ret = fio->bRead;
761     gmx_fio_unlock(fio);
762
763     return ret;
764 }
765
766 int xtc_seek_time(t_fileio* fio, real time, int natoms, gmx_bool bSeekForwardOnly)
767 {
768     int ret;
769
770     gmx_fio_lock(fio);
771     ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
772     gmx_fio_unlock(fio);
773
774     return ret;
775 }