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