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,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.
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.
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.
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.
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.
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.
56 #include "thread_mpi/threads.h"
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"
65 #include "gmxfio_impl.h"
67 /* This is the new improved and thread safe version of gmxfio. */
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;
75 /* this mutex locks the open_files structure so that no two threads can
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;
86 using Lock = gmx::lock_guard<gmx::Mutex>;
88 /******************************************************************
92 ******************************************************************/
94 static int gmx_fio_int_flush(t_fileio* fio)
100 rc = fflush(fio->fp);
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)
110 tMPI_Lock_lock(&(fio->mtx));
112 /* unlock the mutex associated with this fio. */
113 void gmx_fio_unlock(t_fileio* fio)
115 tMPI_Lock_unlock(&(fio->mtx));
118 /* make a dummy head element, assuming we locked everything. */
119 static void gmx_fio_make_dummy()
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));
133 /***********************************************************************
135 * FILE LIST OPERATIONS
137 ***********************************************************************/
140 /* insert a new t_fileio into the list */
141 static void gmx_fio_insert(t_fileio* fio)
144 Lock openFilesLock(open_file_mutex);
145 gmx_fio_make_dummy();
147 /* and lock the fio we got and the list's head **/
149 gmx_fio_lock(open_files);
150 prev = open_files->prev;
151 /* lock the element after the current one */
152 if (prev != open_files)
157 /* now do the actual insertion: */
158 fio->next = open_files;
159 open_files->prev = fio;
163 /* now unlock all our locks */
164 if (prev != open_files)
166 gmx_fio_unlock(prev);
168 gmx_fio_unlock(open_files);
172 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
174 NOTE: We also assume that the open_file_mutex has been locked */
175 static void gmx_fio_remove(t_fileio* fio)
177 /* lock prev, because we're changing it */
178 gmx_fio_lock(fio->prev);
180 /* now set the prev's pointer */
181 fio->prev->next = fio->next;
182 gmx_fio_unlock(fio->prev);
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);
189 /* and make sure we point nowhere in particular */
190 fio->next = fio->prev = fio;
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()
200 gmx_fio_make_dummy();
202 gmx_fio_lock(open_files);
203 ret = open_files->next;
206 /* check whether there were any to begin with */
207 if (ret == open_files)
209 /* after this, the open_file pointer should never change */
214 gmx_fio_lock(open_files->next);
216 gmx_fio_unlock(open_files);
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)
230 /* check if that was the last one */
231 if (fio->next == open_files)
244 /* Stop looping through the open_files. Assumes open_file_mutex is locked. */
245 static void gmx_fio_stop_getting_next(t_fileio* fio)
251 /*****************************************************************
255 *****************************************************************/
256 t_fileio* gmx_fio_open(const char* fn, const char* mode)
258 t_fileio* fio = nullptr;
260 gmx_bool bRead, bReadWrite;
262 /* sanitize the mode string */
263 if (std::strncmp(mode, "r+", 2) == 0)
265 std::strcpy(newmode, "r+");
267 else if (mode[0] == 'r')
269 std::strcpy(newmode, "r");
271 else if (strncmp(mode, "w+", 2) == 0)
273 std::strcpy(newmode, "w+");
275 else if (mode[0] == 'w')
277 std::strcpy(newmode, "w");
279 else if (strncmp(mode, "a+", 2) == 0)
281 std::strcpy(newmode, "a+");
283 else if (mode[0] == 'a')
285 std::strcpy(newmode, "a");
289 gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
292 /* Check if it should be opened as a binary file */
293 if (!ftp_is_text(fn2ftp(fn)))
295 strcat(newmode, "b");
298 fio = new t_fileio{};
299 tMPI_Lock_init(&(fio->mtx));
300 bRead = (newmode[0] == 'r' && newmode[1] != '+');
301 bReadWrite = (newmode[1] == '+');
306 if (fn2ftp(fn) == efTNG)
308 gmx_incons("gmx_fio_open may not be used to open TNG files");
310 fio->iFTP = fn2ftp(fn);
311 fio->fn = gmx_strdup(fn);
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))
317 /* determine the XDR direction */
318 if (newmode[0] == 'w' || newmode[0] == 'a')
320 fio->xdrmode = XDR_ENCODE;
324 fio->xdrmode = XDR_DECODE;
327 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
330 /* for appending seek to end of file to make sure ftell gives correct position
331 * important for checkpointing */
332 if (newmode[0] == 'a')
334 gmx_fseek(fio->fp, 0, SEEK_END);
339 gmx_fatal(FARGS, "Cannot open file with NULL filename string");
343 fio->bReadWrite = bReadWrite;
344 fio->bDouble = (sizeof(real) == sizeof(double));
346 /* and now insert this file into the list of open files. */
351 static int gmx_fio_close_locked(t_fileio* fio)
355 if (fio->xdr != nullptr)
357 xdr_destroy(fio->xdr);
361 if (fio->fp != nullptr)
363 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
369 int gmx_fio_close(t_fileio* fio)
373 Lock openFilesLock(open_file_mutex);
376 /* first remove it from the list */
378 rc = gmx_fio_close_locked(fio);
387 /* close only fp but keep FIO entry. */
388 int gmx_fio_fp_close(t_fileio* fio)
392 if (fio->xdr == nullptr)
394 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
402 FILE* gmx_fio_fopen(const char* fn, const char* mode)
407 fio = gmx_fio_open(fn, mode);
415 int gmx_fio_fclose(FILE* fp)
420 Lock openFilesLock(open_file_mutex);
421 cur = gmx_fio_get_first();
426 rc = gmx_fio_close_locked(cur);
428 gmx_fio_stop_getting_next(cur);
433 cur = gmx_fio_get_next(cur);
439 //! Helper struct for returning the MD5 checksum and the amount of the file that contributed to it.
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;
448 /*! \brief Internal variant of get_file_md5 that operates on a locked
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)
455 /*1MB: large size important to catch almost identical files */
456 constexpr size_t maximumChecksumInputSize = 1048576;
458 gmx_off_t readLength;
459 gmx_off_t seekOffset;
461 seekOffset = offset - maximumChecksumInputSize;
466 readLength = offset - seekOffset;
470 // It's not an error if the file isn't open.
473 if (!fio->bReadWrite)
475 // It's not an error if the file is open in the wrong mode.
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
487 if (gmx_fseek(fio->fp, seekOffset, SEEK_SET))
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);
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)
501 // Read an unexpected length. This is not a fatal error; the
502 // md5sum check to prevent overwriting files is not vital.
505 fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn, strerror(errno));
507 else if (!feof(fio->fp))
509 fprintf(stderr, "\nTrying to get md5sum: Unknown reason for short read: %s\n", fio->fn);
512 gmx_fseek(fio->fp, 0, SEEK_END);
515 // Return the file position to the end of the file.
516 gmx_fseek(fio->fp, 0, SEEK_END);
520 fprintf(debug, "chksum %s readlen %ld\n", fio->fn, static_cast<long int>(readLength));
523 gmx_md5_init(&state);
524 gmx_md5_append(&state, buf.data(), readLength);
525 *checksum = gmx_md5_finish(&state);
531 * fio: file to compute md5 for
532 * offset: starting pointer of region to use for md5
533 * digest: return array of md5 sum
535 int gmx_fio_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum)
540 ret = gmx_fio_int_get_file_md5(fio, offset, checksum);
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)
549 /* Flush the file, so we are sure it is written */
550 if (gmx_fio_int_flush(fio))
553 sprintf(buf, "Cannot write file '%s'; maybe you are out of disk space?", fio->fn);
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...
563 *offset = gmx_ftell(fio->fp);
568 std::vector<gmx_file_position_t> gmx_fio_get_output_file_positions()
570 std::vector<gmx_file_position_t> outputfiles;
573 Lock openFilesLock(open_file_mutex);
574 cur = gmx_fio_get_first();
577 /* Skip the checkpoint files themselves, since they could be open when
578 we call this routine... */
579 if (!cur->bRead && cur->iFTP != efCPT)
581 outputfiles.emplace_back();
583 std::strncpy(outputfiles.back().filename, cur->fn, STRLEN - 1);
585 /* Get the file position */
586 gmx_fio_int_get_file_position(cur, &outputfiles.back().offset);
589 outputfiles.back().checksumSize = gmx_fio_int_get_file_md5(
590 cur, outputfiles.back().offset, &outputfiles.back().checksum);
594 cur = gmx_fio_get_next(cur);
601 char* gmx_fio_getname(t_fileio* fio)
611 int gmx_fio_getftp(t_fileio* fio)
622 void gmx_fio_rewind(t_fileio* fio)
628 xdr_destroy(fio->xdr);
630 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
640 int gmx_fio_flush(t_fileio* fio)
645 ret = gmx_fio_int_flush(fio);
652 static int gmx_fio_int_fsync(t_fileio* fio)
658 rc = gmx_fsync(fio->fp);
664 int gmx_fio_fsync(t_fileio* fio)
669 rc = gmx_fio_int_fsync(fio);
676 t_fileio* gmx_fio_all_output_fsync()
678 t_fileio* ret = nullptr;
681 Lock openFilesLock(open_file_mutex);
682 cur = gmx_fio_get_first();
687 /* if any of them fails, return failure code */
688 int rc = gmx_fio_int_fsync(cur);
694 cur = gmx_fio_get_next(cur);
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. */
703 /* again, fahcore defines HAVE_FSYNC and fsync() */
704 fsync(STDOUT_FILENO);
705 fsync(STDERR_FILENO);
712 gmx_off_t gmx_fio_ftell(t_fileio* fio)
719 ret = gmx_ftell(fio->fp);
725 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
732 rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
742 FILE* gmx_fio_getfp(t_fileio* fio)
755 gmx_bool gmx_fio_getread(t_fileio* fio)
766 int xtc_seek_time(t_fileio* fio, real time, int natoms, gmx_bool bSeekForwardOnly)
771 ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);