2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
58 #include "thread_mpi/threads.h"
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"
66 #include "gmxfio_impl.h"
68 /* This is the new improved and thread safe version of gmxfio. */
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 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
74 static t_fileio* open_files = nullptr;
77 /* this mutex locks the open_files structure so that no two threads can
80 For now, we use this as a coarse grained lock on all file
81 insertion/deletion operations because it makes avoiding deadlocks
82 easier, and adds almost no overhead: the only overhead is during
83 opening and closing of files, or during global operations like
84 iterating along all open files. All these cases should be rare
85 during the simulation. */
86 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
87 static std::mutex open_file_mutex;
89 using Lock = std::lock_guard<std::mutex>;
91 /******************************************************************
95 ******************************************************************/
97 static int gmx_fio_int_flush(t_fileio* fio)
103 rc = fflush(fio->fp);
109 /* lock the mutex associated with this fio. This needs to be done for every
110 type of access to the fio's elements. */
111 void gmx_fio_lock(t_fileio* fio)
113 tMPI_Lock_lock(&(fio->mtx));
115 /* unlock the mutex associated with this fio. */
116 void gmx_fio_unlock(t_fileio* fio)
118 tMPI_Lock_unlock(&(fio->mtx));
121 /* make a dummy head element, assuming we locked everything. */
122 static void gmx_fio_make_dummy()
126 open_files = new t_fileio{};
127 open_files->fp = nullptr;
128 open_files->fn = nullptr;
129 open_files->next = open_files;
130 open_files->prev = open_files;
131 tMPI_Lock_init(&(open_files->mtx));
136 /***********************************************************************
138 * FILE LIST OPERATIONS
140 ***********************************************************************/
143 /* insert a new t_fileio into the list */
144 static void gmx_fio_insert(t_fileio* fio)
147 Lock openFilesLock(open_file_mutex);
148 gmx_fio_make_dummy();
150 /* and lock the fio we got and the list's head **/
152 gmx_fio_lock(open_files);
153 prev = open_files->prev;
154 /* lock the element after the current one */
155 if (prev != open_files)
160 /* now do the actual insertion: */
161 fio->next = open_files;
162 open_files->prev = fio;
166 /* now unlock all our locks */
167 if (prev != open_files)
169 gmx_fio_unlock(prev);
171 gmx_fio_unlock(open_files);
175 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
177 NOTE: We also assume that the open_file_mutex has been locked */
178 static void gmx_fio_remove(t_fileio* fio)
180 /* lock prev, because we're changing it */
181 gmx_fio_lock(fio->prev);
183 /* now set the prev's pointer */
184 fio->prev->next = fio->next;
185 gmx_fio_unlock(fio->prev);
187 /* with the next ptr, we can simply lock while the original was locked */
188 gmx_fio_lock(fio->next);
189 fio->next->prev = fio->prev;
190 gmx_fio_unlock(fio->next);
192 /* and make sure we point nowhere in particular */
193 fio->next = fio->prev = fio;
197 /* get the first open file, or NULL if there is none.
198 Returns a locked fio. Assumes open_files_mutex is locked. */
199 static t_fileio* gmx_fio_get_first()
203 gmx_fio_make_dummy();
205 gmx_fio_lock(open_files);
206 ret = open_files->next;
209 /* check whether there were any to begin with */
210 if (ret == open_files)
212 /* after this, the open_file pointer should never change */
217 gmx_fio_lock(open_files->next);
219 gmx_fio_unlock(open_files);
225 /* get the next open file, or NULL if there is none.
226 Unlocks the previous fio and locks the next one.
227 Assumes open_file_mutex is locked. */
228 static t_fileio* gmx_fio_get_next(t_fileio* fio)
233 /* check if that was the last one */
234 if (fio->next == open_files)
247 /* Stop looping through the open_files. Assumes open_file_mutex is locked. */
248 static void gmx_fio_stop_getting_next(t_fileio* fio)
254 /*****************************************************************
258 *****************************************************************/
259 t_fileio* gmx_fio_open(const char* fn, const char* mode)
261 t_fileio* fio = nullptr;
263 gmx_bool bRead, bReadWrite;
265 /* sanitize the mode string */
266 if (std::strncmp(mode, "r+", 2) == 0)
268 std::strcpy(newmode, "r+");
270 else if (mode[0] == 'r')
272 std::strcpy(newmode, "r");
274 else if (strncmp(mode, "w+", 2) == 0)
276 std::strcpy(newmode, "w+");
278 else if (mode[0] == 'w')
280 std::strcpy(newmode, "w");
282 else if (strncmp(mode, "a+", 2) == 0)
284 std::strcpy(newmode, "a+");
286 else if (mode[0] == 'a')
288 std::strcpy(newmode, "a");
292 gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
295 /* Check if it should be opened as a binary file */
296 if (!ftp_is_text(fn2ftp(fn)))
298 strcat(newmode, "b");
301 fio = new t_fileio{};
302 tMPI_Lock_init(&(fio->mtx));
303 bRead = (newmode[0] == 'r' && newmode[1] != '+');
304 bReadWrite = (newmode[1] == '+');
309 if (fn2ftp(fn) == efTNG)
311 gmx_incons("gmx_fio_open may not be used to open TNG files");
313 fio->iFTP = fn2ftp(fn);
314 fio->fn = gmx_strdup(fn);
316 fio->fp = gmx_ffopen(fn, newmode);
317 /* If this file type is in the list of XDR files, open it like that */
318 if (ftp_is_xdr(fio->iFTP))
320 /* determine the XDR direction */
321 if (newmode[0] == 'w' || newmode[0] == 'a')
323 fio->xdrmode = XDR_ENCODE;
327 fio->xdrmode = XDR_DECODE;
330 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
333 /* for appending seek to end of file to make sure ftell gives correct position
334 * important for checkpointing */
335 if (newmode[0] == 'a')
337 gmx_fseek(fio->fp, 0, SEEK_END);
342 gmx_fatal(FARGS, "Cannot open file with NULL filename string");
346 fio->bReadWrite = bReadWrite;
347 fio->bDouble = (sizeof(real) == sizeof(double));
349 /* and now insert this file into the list of open files. */
354 static int gmx_fio_close_locked(t_fileio* fio)
358 if (fio->xdr != nullptr)
360 xdr_destroy(fio->xdr);
364 if (fio->fp != nullptr)
366 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
372 int gmx_fio_close(t_fileio* fio)
376 Lock openFilesLock(open_file_mutex);
379 /* first remove it from the list */
381 rc = gmx_fio_close_locked(fio);
390 /* close only fp but keep FIO entry. */
391 int gmx_fio_fp_close(t_fileio* fio)
395 if (fio->xdr == nullptr)
397 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
405 FILE* gmx_fio_fopen(const char* fn, const char* mode)
410 fio = gmx_fio_open(fn, mode);
418 int gmx_fio_fclose(FILE* fp)
423 Lock openFilesLock(open_file_mutex);
424 cur = gmx_fio_get_first();
429 rc = gmx_fio_close_locked(cur);
431 gmx_fio_stop_getting_next(cur);
436 cur = gmx_fio_get_next(cur);
442 //! Helper struct for returning the MD5 checksum and the amount of the file that contributed to it.
445 //! Checksum md5 digest.
446 std::array<unsigned char, 16> checksum;
447 //! The length of the file that contributed to the digest.
448 gmx_off_t readLength;
451 /*! \brief Internal variant of get_file_md5 that operates on a locked
454 * \return -1 any time a checksum cannot be computed, otherwise the
455 * length of the data from which the checksum was computed. */
456 static int gmx_fio_int_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum)
458 /*1MB: large size important to catch almost identical files */
459 constexpr size_t maximumChecksumInputSize = 1048576;
461 gmx_off_t readLength;
462 gmx_off_t seekOffset;
464 seekOffset = offset - maximumChecksumInputSize;
469 readLength = offset - seekOffset;
473 // It's not an error if the file isn't open.
476 if (!fio->bReadWrite)
478 // It's not an error if the file is open in the wrong mode.
480 // TODO It is unclear why this check exists. The bReadWrite
481 // flag is true when the file-opening mode included "+" but we
482 // only need read and seek to be able to compute the
483 // md5sum. Other requirements (e.g. that we can truncate when
484 // doing an appending restart) should be expressed in a
485 // different way, but it is unclear whether that is part of
490 if (gmx_fseek(fio->fp, seekOffset, SEEK_SET))
492 // It's not an error if file seeking fails. (But it could be
493 // an issue when moving a checkpoint from one platform to
494 // another, when they differ in their support for seeking, and
495 // so can't agree on a checksum for appending).
496 gmx_fseek(fio->fp, 0, SEEK_END);
500 std::vector<unsigned char> buf(maximumChecksumInputSize);
501 // The fread puts the file position back to offset.
502 if (static_cast<gmx_off_t>(fread(buf.data(), 1, readLength, fio->fp)) != readLength)
504 // Read an unexpected length. This is not a fatal error; the
505 // md5sum check to prevent overwriting files is not vital.
508 fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn, strerror(errno));
510 else if (!feof(fio->fp))
512 fprintf(stderr, "\nTrying to get md5sum: Unknown reason for short read: %s\n", fio->fn);
515 gmx_fseek(fio->fp, 0, SEEK_END);
518 // Return the file position to the end of the file.
519 gmx_fseek(fio->fp, 0, SEEK_END);
523 fprintf(debug, "chksum %s readlen %ld\n", fio->fn, static_cast<long int>(readLength));
526 gmx_md5_init(&state);
527 gmx_md5_append(&state, buf.data(), readLength);
528 *checksum = gmx_md5_finish(&state);
534 * fio: file to compute md5 for
535 * offset: starting pointer of region to use for md5
536 * digest: return array of md5 sum
538 int gmx_fio_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum)
543 ret = gmx_fio_int_get_file_md5(fio, offset, checksum);
549 /* The fio_mutex should ALWAYS be locked when this function is called */
550 static int gmx_fio_int_get_file_position(t_fileio* fio, gmx_off_t* offset)
552 /* Flush the file, so we are sure it is written */
553 if (gmx_fio_int_flush(fio))
556 sprintf(buf, "Cannot write file '%s'; maybe you are out of disk space?", fio->fn);
560 /* We cannot count on XDR being able to write 64-bit integers,
561 so separate into high/low 32-bit values.
562 In case the filesystem has 128-bit offsets we only care
563 about the first 64 bits - we'll have to fix
564 this when exabyte-size output files are common...
566 *offset = gmx_ftell(fio->fp);
571 std::vector<gmx_file_position_t> gmx_fio_get_output_file_positions()
573 std::vector<gmx_file_position_t> outputfiles;
576 Lock openFilesLock(open_file_mutex);
577 cur = gmx_fio_get_first();
580 /* Skip the checkpoint files themselves, since they could be open when
581 we call this routine... */
582 if (!cur->bRead && cur->iFTP != efCPT)
584 outputfiles.emplace_back();
586 std::strncpy(outputfiles.back().filename, cur->fn, STRLEN - 1);
588 /* Get the file position */
589 gmx_fio_int_get_file_position(cur, &outputfiles.back().offset);
592 outputfiles.back().checksumSize = gmx_fio_int_get_file_md5(
593 cur, outputfiles.back().offset, &outputfiles.back().checksum);
597 cur = gmx_fio_get_next(cur);
604 char* gmx_fio_getname(t_fileio* fio)
614 int gmx_fio_getftp(t_fileio* fio)
625 void gmx_fio_rewind(t_fileio* fio)
631 xdr_destroy(fio->xdr);
633 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
643 int gmx_fio_flush(t_fileio* fio)
648 ret = gmx_fio_int_flush(fio);
655 static int gmx_fio_int_fsync(t_fileio* fio)
661 rc = gmx_fsync(fio->fp);
667 int gmx_fio_fsync(t_fileio* fio)
672 rc = gmx_fio_int_fsync(fio);
679 t_fileio* gmx_fio_all_output_fsync()
681 t_fileio* ret = nullptr;
684 Lock openFilesLock(open_file_mutex);
685 cur = gmx_fio_get_first();
690 /* if any of them fails, return failure code */
691 int rc = gmx_fio_int_fsync(cur);
697 cur = gmx_fio_get_next(cur);
700 /* in addition, we force these to be written out too, if they're being
701 redirected. We don't check for errors because errors most likely mean
702 that they're not redirected. */
706 /* again, fahcore defines HAVE_FSYNC and fsync() */
707 fsync(STDOUT_FILENO);
708 fsync(STDERR_FILENO);
715 gmx_off_t gmx_fio_ftell(t_fileio* fio)
722 ret = gmx_ftell(fio->fp);
728 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
735 rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
745 FILE* gmx_fio_getfp(t_fileio* fio)
758 gmx_bool gmx_fio_getread(t_fileio* fio)
769 int xtc_seek_time(t_fileio* fio, real time, int natoms, gmx_bool bSeekForwardOnly)
774 ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);