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, 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.
57 #include "thread_mpi/threads.h"
59 #include "gromacs/fileio/filetypes.h"
60 #include "gromacs/fileio/md5.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/futil.h"
63 #include "gromacs/utility/mutex.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 static t_fileio
* open_files
= nullptr;
76 /* this mutex locks the open_files structure so that no two threads can
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 gmx::Mutex open_file_mutex
;
87 using Lock
= gmx::lock_guard
<gmx::Mutex
>;
89 /******************************************************************
93 ******************************************************************/
95 static int gmx_fio_int_flush(t_fileio
* fio
)
101 rc
= fflush(fio
->fp
);
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
)
111 tMPI_Lock_lock(&(fio
->mtx
));
113 /* unlock the mutex associated with this fio. */
114 void gmx_fio_unlock(t_fileio
* fio
)
116 tMPI_Lock_unlock(&(fio
->mtx
));
119 /* make a dummy head element, assuming we locked everything. */
120 static void gmx_fio_make_dummy()
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
));
134 /***********************************************************************
136 * FILE LIST OPERATIONS
138 ***********************************************************************/
141 /* insert a new t_fileio into the list */
142 static void gmx_fio_insert(t_fileio
* fio
)
145 Lock
openFilesLock(open_file_mutex
);
146 gmx_fio_make_dummy();
148 /* and lock the fio we got and the list's head **/
150 gmx_fio_lock(open_files
);
151 prev
= open_files
->prev
;
152 /* lock the element after the current one */
153 if (prev
!= open_files
)
158 /* now do the actual insertion: */
159 fio
->next
= open_files
;
160 open_files
->prev
= fio
;
164 /* now unlock all our locks */
165 if (prev
!= open_files
)
167 gmx_fio_unlock(prev
);
169 gmx_fio_unlock(open_files
);
173 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
175 NOTE: We also assume that the open_file_mutex has been locked */
176 static void gmx_fio_remove(t_fileio
* fio
)
178 /* lock prev, because we're changing it */
179 gmx_fio_lock(fio
->prev
);
181 /* now set the prev's pointer */
182 fio
->prev
->next
= fio
->next
;
183 gmx_fio_unlock(fio
->prev
);
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
);
190 /* and make sure we point nowhere in particular */
191 fio
->next
= fio
->prev
= fio
;
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()
201 gmx_fio_make_dummy();
203 gmx_fio_lock(open_files
);
204 ret
= open_files
->next
;
207 /* check whether there were any to begin with */
208 if (ret
== open_files
)
210 /* after this, the open_file pointer should never change */
215 gmx_fio_lock(open_files
->next
);
217 gmx_fio_unlock(open_files
);
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
)
231 /* check if that was the last one */
232 if (fio
->next
== open_files
)
245 /* Stop looping through the open_files. Assumes open_file_mutex is locked. */
246 static void gmx_fio_stop_getting_next(t_fileio
* fio
)
252 /*****************************************************************
256 *****************************************************************/
257 t_fileio
* gmx_fio_open(const char* fn
, const char* mode
)
259 t_fileio
* fio
= nullptr;
261 gmx_bool bRead
, bReadWrite
;
263 /* sanitize the mode string */
264 if (std::strncmp(mode
, "r+", 2) == 0)
266 std::strcpy(newmode
, "r+");
268 else if (mode
[0] == 'r')
270 std::strcpy(newmode
, "r");
272 else if (strncmp(mode
, "w+", 2) == 0)
274 std::strcpy(newmode
, "w+");
276 else if (mode
[0] == 'w')
278 std::strcpy(newmode
, "w");
280 else if (strncmp(mode
, "a+", 2) == 0)
282 std::strcpy(newmode
, "a+");
284 else if (mode
[0] == 'a')
286 std::strcpy(newmode
, "a");
290 gmx_fatal(FARGS
, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode
);
293 /* Check if it should be opened as a binary file */
294 if (!ftp_is_text(fn2ftp(fn
)))
296 strcat(newmode
, "b");
299 fio
= new t_fileio
{};
300 tMPI_Lock_init(&(fio
->mtx
));
301 bRead
= (newmode
[0] == 'r' && newmode
[1] != '+');
302 bReadWrite
= (newmode
[1] == '+');
307 if (fn2ftp(fn
) == efTNG
)
309 gmx_incons("gmx_fio_open may not be used to open TNG files");
311 fio
->iFTP
= fn2ftp(fn
);
312 fio
->fn
= gmx_strdup(fn
);
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
))
318 /* determine the XDR direction */
319 if (newmode
[0] == 'w' || newmode
[0] == 'a')
321 fio
->xdrmode
= XDR_ENCODE
;
325 fio
->xdrmode
= XDR_DECODE
;
328 xdrstdio_create(fio
->xdr
, fio
->fp
, fio
->xdrmode
);
331 /* for appending seek to end of file to make sure ftell gives correct position
332 * important for checkpointing */
333 if (newmode
[0] == 'a')
335 gmx_fseek(fio
->fp
, 0, SEEK_END
);
340 gmx_fatal(FARGS
, "Cannot open file with NULL filename string");
344 fio
->bReadWrite
= bReadWrite
;
345 fio
->bDouble
= (sizeof(real
) == sizeof(double));
347 /* and now insert this file into the list of open files. */
352 static int gmx_fio_close_locked(t_fileio
* fio
)
356 if (fio
->xdr
!= nullptr)
358 xdr_destroy(fio
->xdr
);
362 if (fio
->fp
!= nullptr)
364 rc
= gmx_ffclose(fio
->fp
); /* fclose returns 0 if happy */
370 int gmx_fio_close(t_fileio
* fio
)
374 Lock
openFilesLock(open_file_mutex
);
377 /* first remove it from the list */
379 rc
= gmx_fio_close_locked(fio
);
388 /* close only fp but keep FIO entry. */
389 int gmx_fio_fp_close(t_fileio
* fio
)
393 if (fio
->xdr
== nullptr)
395 rc
= gmx_ffclose(fio
->fp
); /* fclose returns 0 if happy */
403 FILE* gmx_fio_fopen(const char* fn
, const char* mode
)
408 fio
= gmx_fio_open(fn
, mode
);
416 int gmx_fio_fclose(FILE* fp
)
421 Lock
openFilesLock(open_file_mutex
);
422 cur
= gmx_fio_get_first();
427 rc
= gmx_fio_close_locked(cur
);
429 gmx_fio_stop_getting_next(cur
);
434 cur
= gmx_fio_get_next(cur
);
440 //! Helper struct for returning the MD5 checksum and the amount of the file that contributed to it.
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
;
449 /*! \brief Internal variant of get_file_md5 that operates on a locked
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
)
456 /*1MB: large size important to catch almost identical files */
457 constexpr size_t maximumChecksumInputSize
= 1048576;
459 gmx_off_t readLength
;
460 gmx_off_t seekOffset
;
462 seekOffset
= offset
- maximumChecksumInputSize
;
467 readLength
= offset
- seekOffset
;
471 // It's not an error if the file isn't open.
474 if (!fio
->bReadWrite
)
476 // It's not an error if the file is open in the wrong mode.
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
488 if (gmx_fseek(fio
->fp
, seekOffset
, SEEK_SET
))
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
);
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
)
502 // Read an unexpected length. This is not a fatal error; the
503 // md5sum check to prevent overwriting files is not vital.
506 fprintf(stderr
, "\nTrying to get md5sum: %s: %s\n", fio
->fn
, strerror(errno
));
508 else if (!feof(fio
->fp
))
510 fprintf(stderr
, "\nTrying to get md5sum: Unknown reason for short read: %s\n", fio
->fn
);
513 gmx_fseek(fio
->fp
, 0, SEEK_END
);
516 // Return the file position to the end of the file.
517 gmx_fseek(fio
->fp
, 0, SEEK_END
);
521 fprintf(debug
, "chksum %s readlen %ld\n", fio
->fn
, static_cast<long int>(readLength
));
524 gmx_md5_init(&state
);
525 gmx_md5_append(&state
, buf
.data(), readLength
);
526 *checksum
= gmx_md5_finish(&state
);
532 * fio: file to compute md5 for
533 * offset: starting pointer of region to use for md5
534 * digest: return array of md5 sum
536 int gmx_fio_get_file_md5(t_fileio
* fio
, gmx_off_t offset
, std::array
<unsigned char, 16>* checksum
)
541 ret
= gmx_fio_int_get_file_md5(fio
, offset
, checksum
);
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
)
550 /* Flush the file, so we are sure it is written */
551 if (gmx_fio_int_flush(fio
))
554 sprintf(buf
, "Cannot write file '%s'; maybe you are out of disk space?", fio
->fn
);
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...
564 *offset
= gmx_ftell(fio
->fp
);
569 std::vector
<gmx_file_position_t
> gmx_fio_get_output_file_positions()
571 std::vector
<gmx_file_position_t
> outputfiles
;
574 Lock
openFilesLock(open_file_mutex
);
575 cur
= gmx_fio_get_first();
578 /* Skip the checkpoint files themselves, since they could be open when
579 we call this routine... */
580 if (!cur
->bRead
&& cur
->iFTP
!= efCPT
)
582 outputfiles
.emplace_back();
584 std::strncpy(outputfiles
.back().filename
, cur
->fn
, STRLEN
- 1);
586 /* Get the file position */
587 gmx_fio_int_get_file_position(cur
, &outputfiles
.back().offset
);
590 outputfiles
.back().checksumSize
= gmx_fio_int_get_file_md5(
591 cur
, outputfiles
.back().offset
, &outputfiles
.back().checksum
);
595 cur
= gmx_fio_get_next(cur
);
602 char* gmx_fio_getname(t_fileio
* fio
)
612 int gmx_fio_getftp(t_fileio
* fio
)
623 void gmx_fio_rewind(t_fileio
* fio
)
629 xdr_destroy(fio
->xdr
);
631 xdrstdio_create(fio
->xdr
, fio
->fp
, fio
->xdrmode
);
641 int gmx_fio_flush(t_fileio
* fio
)
646 ret
= gmx_fio_int_flush(fio
);
653 static int gmx_fio_int_fsync(t_fileio
* fio
)
659 rc
= gmx_fsync(fio
->fp
);
665 int gmx_fio_fsync(t_fileio
* fio
)
670 rc
= gmx_fio_int_fsync(fio
);
677 t_fileio
* gmx_fio_all_output_fsync()
679 t_fileio
* ret
= nullptr;
682 Lock
openFilesLock(open_file_mutex
);
683 cur
= gmx_fio_get_first();
688 /* if any of them fails, return failure code */
689 int rc
= gmx_fio_int_fsync(cur
);
695 cur
= gmx_fio_get_next(cur
);
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. */
704 /* again, fahcore defines HAVE_FSYNC and fsync() */
705 fsync(STDOUT_FILENO
);
706 fsync(STDERR_FILENO
);
713 gmx_off_t
gmx_fio_ftell(t_fileio
* fio
)
720 ret
= gmx_ftell(fio
->fp
);
726 int gmx_fio_seek(t_fileio
* fio
, gmx_off_t fpos
)
733 rc
= gmx_fseek(fio
->fp
, fpos
, SEEK_SET
);
743 FILE* gmx_fio_getfp(t_fileio
* fio
)
756 gmx_bool
gmx_fio_getread(t_fileio
* fio
)
767 int xtc_seek_time(t_fileio
* fio
, real time
, int natoms
, gmx_bool bSeekForwardOnly
)
772 ret
= xdr_xtc_seek_time(time
, fio
->fp
, fio
->xdr
, natoms
, bSeekForwardOnly
);