Simplified uniform GPU selection in CMake
[gromacs.git] / src / gromacs / fileio / gmxfio.cpp
blobcad75e64eb44fadf5b60f3d1dabbfaf8732f21e7
1 /*
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.
38 #include "gmxpre.h"
40 #include "gmxfio.h"
42 #include "config.h"
44 #include <cerrno>
45 #include <cstdio>
46 #include <cstring>
48 #include <vector>
50 #if HAVE_IO_H
51 # include <io.h>
52 #endif
53 #ifdef HAVE_UNISTD_H
54 # include <unistd.h>
55 #endif
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
77 modify it.
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 /******************************************************************
91 * Internal functions:
93 ******************************************************************/
95 static int gmx_fio_int_flush(t_fileio* fio)
97 int rc = 0;
99 if (fio->fp)
101 rc = fflush(fio->fp);
104 return rc;
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()
122 if (!open_files)
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)
144 t_fileio* prev;
145 Lock openFilesLock(open_file_mutex);
146 gmx_fio_make_dummy();
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)
155 gmx_fio_lock(prev);
158 /* now do the actual insertion: */
159 fio->next = open_files;
160 open_files->prev = fio;
161 prev->next = fio;
162 fio->prev = prev;
164 /* now unlock all our locks */
165 if (prev != open_files)
167 gmx_fio_unlock(prev);
169 gmx_fio_unlock(open_files);
170 gmx_fio_unlock(fio);
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)
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()
199 t_fileio* ret;
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 */
211 ret = nullptr;
213 else
215 gmx_fio_lock(open_files->next);
217 gmx_fio_unlock(open_files);
220 return ret;
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)
228 t_fileio* ret;
230 ret = fio->next;
231 /* check if that was the last one */
232 if (fio->next == open_files)
234 ret = nullptr;
236 else
238 gmx_fio_lock(ret);
240 gmx_fio_unlock(fio);
242 return ret;
245 /* Stop looping through the open_files. Assumes open_file_mutex is locked. */
246 static void gmx_fio_stop_getting_next(t_fileio* fio)
248 gmx_fio_unlock(fio);
252 /*****************************************************************
254 * EXPORTED SECTION
256 *****************************************************************/
257 t_fileio* gmx_fio_open(const char* fn, const char* mode)
259 t_fileio* fio = nullptr;
260 char newmode[5];
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");
288 else
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] == '+');
303 fio->fp = nullptr;
304 fio->xdr = nullptr;
305 if (fn)
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;
323 else
325 fio->xdrmode = XDR_DECODE;
327 snew(fio->xdr, 1);
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);
338 else
340 gmx_fatal(FARGS, "Cannot open file with NULL filename string");
343 fio->bRead = bRead;
344 fio->bReadWrite = bReadWrite;
345 fio->bDouble = (sizeof(real) == sizeof(double));
347 /* and now insert this file into the list of open files. */
348 gmx_fio_insert(fio);
349 return fio;
352 static int gmx_fio_close_locked(t_fileio* fio)
354 int rc = 0;
356 if (fio->xdr != nullptr)
358 xdr_destroy(fio->xdr);
359 sfree(fio->xdr);
362 if (fio->fp != nullptr)
364 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
367 return rc;
370 int gmx_fio_close(t_fileio* fio)
372 int rc = 0;
374 Lock openFilesLock(open_file_mutex);
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);
382 sfree(fio->fn);
383 delete fio;
385 return rc;
388 /* close only fp but keep FIO entry. */
389 int gmx_fio_fp_close(t_fileio* fio)
391 int rc = 0;
392 gmx_fio_lock(fio);
393 if (fio->xdr == nullptr)
395 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
396 fio->fp = nullptr;
398 gmx_fio_unlock(fio);
400 return rc;
403 FILE* gmx_fio_fopen(const char* fn, const char* mode)
405 FILE* ret;
406 t_fileio* fio;
408 fio = gmx_fio_open(fn, mode);
409 gmx_fio_lock(fio);
410 ret = fio->fp;
411 gmx_fio_unlock(fio);
413 return ret;
416 int gmx_fio_fclose(FILE* fp)
418 t_fileio* cur;
419 int rc = -1;
421 Lock openFilesLock(open_file_mutex);
422 cur = gmx_fio_get_first();
423 while (cur)
425 if (cur->fp == fp)
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;
434 cur = gmx_fio_get_next(cur);
437 return rc;
440 //! Helper struct for returning the MD5 checksum and the amount of the file that contributed to it.
441 struct MD5Checksum
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
450 * file.
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;
458 md5_state_t state;
459 gmx_off_t readLength;
460 gmx_off_t seekOffset;
462 seekOffset = offset - maximumChecksumInputSize;
463 if (seekOffset < 0)
465 seekOffset = 0;
467 readLength = offset - seekOffset;
469 if (!fio->fp)
471 // It's not an error if the file isn't open.
472 return -1;
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
484 // the logic here.
485 return -1;
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);
495 return -1;
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.
504 if (ferror(fio->fp))
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);
514 return -1;
516 // Return the file position to the end of the file.
517 gmx_fseek(fio->fp, 0, SEEK_END);
519 if (debug)
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);
527 return readLength;
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)
538 int ret;
540 gmx_fio_lock(fio);
541 ret = gmx_fio_int_get_file_md5(fio, offset, checksum);
542 gmx_fio_unlock(fio);
544 return ret;
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))
553 char buf[STRLEN];
554 sprintf(buf, "Cannot write file '%s'; maybe you are out of disk space?", fio->fn);
555 gmx_file(buf);
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);
566 return 0;
569 std::vector<gmx_file_position_t> gmx_fio_get_output_file_positions()
571 std::vector<gmx_file_position_t> outputfiles;
572 t_fileio* cur;
574 Lock openFilesLock(open_file_mutex);
575 cur = gmx_fio_get_first();
576 while (cur)
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);
588 if (!GMX_FAHCORE)
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);
598 return outputfiles;
602 char* gmx_fio_getname(t_fileio* fio)
604 char* ret;
605 gmx_fio_lock(fio);
606 ret = fio->fn;
607 gmx_fio_unlock(fio);
609 return ret;
612 int gmx_fio_getftp(t_fileio* fio)
614 int ret;
616 gmx_fio_lock(fio);
617 ret = fio->iFTP;
618 gmx_fio_unlock(fio);
620 return ret;
623 void gmx_fio_rewind(t_fileio* fio)
625 gmx_fio_lock(fio);
627 if (fio->xdr)
629 xdr_destroy(fio->xdr);
630 frewind(fio->fp);
631 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
633 else
635 frewind(fio->fp);
637 gmx_fio_unlock(fio);
641 int gmx_fio_flush(t_fileio* fio)
643 int ret;
645 gmx_fio_lock(fio);
646 ret = gmx_fio_int_flush(fio);
647 gmx_fio_unlock(fio);
649 return ret;
653 static int gmx_fio_int_fsync(t_fileio* fio)
655 int rc = 0;
657 if (fio->fp)
659 rc = gmx_fsync(fio->fp);
661 return rc;
665 int gmx_fio_fsync(t_fileio* fio)
667 int rc;
669 gmx_fio_lock(fio);
670 rc = gmx_fio_int_fsync(fio);
671 gmx_fio_unlock(fio);
673 return rc;
677 t_fileio* gmx_fio_all_output_fsync()
679 t_fileio* ret = nullptr;
680 t_fileio* cur;
682 Lock openFilesLock(open_file_mutex);
683 cur = gmx_fio_get_first();
684 while (cur)
686 if (!cur->bRead)
688 /* if any of them fails, return failure code */
689 int rc = gmx_fio_int_fsync(cur);
690 if (rc != 0 && !ret)
692 ret = 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. */
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
709 return ret;
713 gmx_off_t gmx_fio_ftell(t_fileio* fio)
715 gmx_off_t ret = 0;
717 gmx_fio_lock(fio);
718 if (fio->fp)
720 ret = gmx_ftell(fio->fp);
722 gmx_fio_unlock(fio);
723 return ret;
726 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
728 int rc;
730 gmx_fio_lock(fio);
731 if (fio->fp)
733 rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
735 else
737 gmx_file(fio->fn);
739 gmx_fio_unlock(fio);
740 return rc;
743 FILE* gmx_fio_getfp(t_fileio* fio)
745 FILE* ret = nullptr;
747 gmx_fio_lock(fio);
748 if (fio->fp)
750 ret = fio->fp;
752 gmx_fio_unlock(fio);
753 return ret;
756 gmx_bool gmx_fio_getread(t_fileio* fio)
758 gmx_bool ret;
760 gmx_fio_lock(fio);
761 ret = fio->bRead;
762 gmx_fio_unlock(fio);
764 return ret;
767 int xtc_seek_time(t_fileio* fio, real time, int natoms, gmx_bool bSeekForwardOnly)
769 int ret;
771 gmx_fio_lock(fio);
772 ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
773 gmx_fio_unlock(fio);
775 return ret;