1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
11 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13 * Copyright (c) 2001-2004, The GROMACS development team,
14 * check out http://www.gromacs.org for more information.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * If you want to redistribute modifications, please consider that
22 * scientific software is very special. Version control is crucial -
23 * bugs must be traceable. We will be happy to consider code for
24 * inclusion in the official distribution, but derived work must not
25 * be called official GROMACS. Details are found in the README & COPYING
26 * files - if they are missing, get the official version at www.gromacs.org.
28 * To help us fund GROMACS development, we humbly ask that you cite
29 * the papers on the package - you can find them in the top README file.
31 * For more info, check our website at http://www.gromacs.org
34 * GROningen Mixture of Alchemy and Childrens' Stories
47 #include "gmx_fatal.h"
57 #include "thread_mpi.h"
60 #include "gmxfio_int.h"
62 /* This is the new improved and thread safe version of gmxfio. */
66 /* the list of open files is a linked list, with a dummy element at its head;
67 it is initialized when the first file is opened. */
68 static t_fileio *open_files = NULL;
72 /* this mutex locks the open_files structure so that no two threads can
75 For now, we use this as a coarse grained lock on all file
76 insertion/deletion operations because it makes avoiding deadlocks
77 easier, and adds almost no overhead: the only overhead is during
78 opening and closing of files, or during global operations like
79 iterating along all open files. All these cases should be rare
80 during the simulation. */
81 static tMPI_Thread_mutex_t open_file_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
85 /* These simple lists define the I/O type for these files */
86 static const int ftpXDR[] =
87 { efTPR, efTRR, efEDR, efXTC, efMTX, efCPT };
88 static const int ftpASC[] =
89 { efTPA, efGRO, efPDB };
90 static const int ftpBIN[] =
93 const char *itemstr[eitemNR] =
94 { "[header]", "[inputrec]", "[box]", "[topology]", "[coordinates]",
95 "[velocities]", "[forces]" };
97 const char *eioNames[eioNR] =
98 { "REAL", "INT", "GMX_STE_T", "UCHAR", "NUCHAR", "USHORT", "RVEC", "NRVEC",
103 /* Comment strings for TPA only */
104 const char *comment_str[eitemNR] = {
105 "; The header holds information on the number of atoms etc. and on whether\n"
106 "; certain items are present in the file or not.\n"
109 "; DO NOT EDIT THIS FILE BY HAND\n"
110 "; The GROMACS preprocessor performs a lot of checks on your input that\n"
111 "; you ignore when editing this. Your simulation may crash because of this\n",
112 "; The inputrec holds the parameters for MD such as the number of steps,\n"
113 "; the timestep and the cut-offs.\n",
114 "; The simulation box in nm.\n",
115 "; The topology section describes the topology of the molecules\n"
116 "; i.e. bonds, angles and dihedrals etc. and also holds the force field\n"
118 "; The atomic coordinates in nm\n",
119 "; The atomic velocities in nm/ps\n",
120 "; The forces on the atoms in nm/ps^2\n" };
125 /******************************************************************
127 * Internal functions:
129 ******************************************************************/
131 static int gmx_fio_int_flush(t_fileio* fio)
137 rc = fflush(fio->fp);
141 rc = fflush((FILE *) fio->xdr->x_private);
147 /* returns TRUE if the file type ftp is in the set set */
148 static gmx_bool in_ftpset(int ftp, int nset, const int set[])
154 for (i = 0; (i < nset); i++)
163 extern void gmx_fio_set_comment(t_fileio *fio, const char *comment)
165 fio->comment=comment;
168 extern void gmx_fio_unset_comment(t_fileio *fio)
174 const char *gmx_fio_dbgstr(t_fileio *fio, const char *desc, char *buf)
178 /* set to empty string */
183 snprintf(buf, GMX_FIO_BUFLEN, " ; %s %s", fio->comment ? fio->comment : "", desc);
189 /* check the number of items given against the type */
190 void gmx_fio_check_nitem(t_fileio *fio, int eio, int nitem, const char *file,
193 if ((nitem != 1) && !((eio == eioNRVEC) || (eio == eioNUCHAR)))
195 "nitem (%d) may differ from 1 only for %s or %s, not for %s"
196 "(%s, %d)",nitem,eioNames[eioNUCHAR],eioNames[eioNRVEC],
197 eioNames[eio],file,line);
201 /* output a data type error. */
202 void gmx_fio_fe(t_fileio *fio, int eio, const char *desc,
203 const char *srcfile, int line)
206 gmx_fatal(FARGS, "Trying to %s %s type %d (%s), src %s, line %d",
207 fio->bRead ? "read" : "write",desc,eio,
208 ((eio >= 0) && (eio < eioNR)) ? eioNames[eio] : "unknown",
213 /* set the reader/writer functions based on the file type */
214 static void gmx_fio_set_iotype(t_fileio *fio)
216 if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
219 fio->iotp=&xdr_iotype;
221 gmx_fatal(FARGS,"Sorry, no XDR");
224 else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
226 fio->iotp=&asc_iotype;
228 else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
230 fio->iotp=&bin_iotype;
233 fio->iotp=&dummy_iotype;
237 /* lock the mutex associated with this fio. This needs to be done for every
238 type of access to the fio's elements. */
239 void gmx_fio_lock(t_fileio *fio)
241 #ifdef GMX_THREAD_MPI
242 tMPI_Lock_lock(&(fio->mtx));
245 /* unlock the mutex associated with this fio. */
246 void gmx_fio_unlock(t_fileio *fio)
248 #ifdef GMX_THREAD_MPI
249 tMPI_Lock_unlock(&(fio->mtx));
253 /* make a dummy head element, assuming we locked everything. */
254 static void gmx_fio_make_dummy(void)
261 open_files->next=open_files;
262 open_files->prev=open_files;
263 #ifdef GMX_THREAD_MPI
264 tMPI_Lock_init(&(open_files->mtx));
275 /***********************************************************************
277 * FILE LIST OPERATIONS
279 ***********************************************************************/
282 /* insert a new t_fileio into the list */
283 static void gmx_fio_insert(t_fileio *fio)
286 #ifdef GMX_THREAD_MPI
287 /* first lock the big open_files mutex. */
288 tMPI_Thread_mutex_lock(&open_file_mutex);
290 /* now check whether the dummy element has been allocated,
291 and allocate it if it hasn't */
292 gmx_fio_make_dummy();
294 /* and lock the fio we got and the list's head **/
296 gmx_fio_lock(open_files);
297 prev=open_files->prev;
298 /* lock the element after the current one */
299 if (prev != open_files)
304 /* now do the actual insertion: */
305 fio->next=open_files;
306 open_files->prev=fio;
310 /* now unlock all our locks */
311 if (prev != open_files)
313 gmx_fio_unlock(prev);
315 gmx_fio_unlock(open_files);
318 #ifdef GMX_THREAD_MPI
319 /* now unlock the big open_files mutex. */
320 tMPI_Thread_mutex_unlock(&open_file_mutex);
324 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
326 NOTE: We also assume that the open_file_mutex has been locked */
327 static void gmx_fio_remove(t_fileio *fio)
331 /* lock prev, because we're changing it */
332 gmx_fio_lock(fio->prev);
334 /* now set the prev's pointer */
335 fio->prev->next=fio->next;
336 gmx_fio_unlock(fio->prev);
338 /* with the next ptr, we can simply lock while the original was locked */
339 gmx_fio_lock(fio->next);
340 fio->next->prev=fio->prev;
341 gmx_fio_unlock(fio->next);
343 /* and make sure we point nowhere in particular */
344 fio->next=fio->prev=fio;
348 /* get the first open file, or NULL if there is none.
349 Returns a locked fio. */
350 static t_fileio *gmx_fio_get_first(void)
353 /* first lock the big open_files mutex and the dummy's mutex */
355 #ifdef GMX_THREAD_MPI
356 /* first lock the big open_files mutex. */
357 tMPI_Thread_mutex_lock(&open_file_mutex);
359 gmx_fio_make_dummy();
361 gmx_fio_lock(open_files);
362 ret=open_files->next;
365 /* check whether there were any to begin with */
368 /* after this, the open_file pointer should never change */
373 gmx_fio_lock(open_files->next);
375 gmx_fio_unlock(open_files);
381 /* get the next open file, or NULL if there is none.
382 Unlocks the previous fio and locks the next one. */
383 static t_fileio *gmx_fio_get_next(t_fileio *fio)
388 /* check if that was the last one */
389 if (fio->next==open_files)
392 #ifdef GMX_THREAD_MPI
393 tMPI_Thread_mutex_unlock(&open_file_mutex);
405 /* Stop looping through the open_files. Unlocks the global lock. */
406 static void gmx_fio_stop_getting_next(t_fileio *fio)
409 #ifdef GMX_THREAD_MPI
410 tMPI_Thread_mutex_unlock(&open_file_mutex);
417 /*****************************************************************
421 *****************************************************************/
422 t_fileio *gmx_fio_open(const char *fn, const char *mode)
424 t_fileio *fio = NULL;
427 gmx_bool bRead, bReadWrite;
430 if (fn2ftp(fn) == efTPA)
432 strcpy(newmode, mode);
436 /* sanitize the mode string */
437 if (strncmp(mode, "r+", 2) == 0)
439 strcpy(newmode, "r+");
441 else if (mode[0] == 'r')
443 strcpy(newmode, "r");
445 else if (strncmp(mode, "w+", 2) == 0)
447 strcpy(newmode, "w+");
449 else if (mode[0] == 'w')
451 strcpy(newmode, "w");
453 else if (strncmp(mode, "a+", 2) == 0)
455 strcpy(newmode, "a+");
457 else if (mode[0] == 'a')
459 strcpy(newmode, "a");
463 gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'",mode);
467 /* Check if it should be opened as a binary file */
468 if (strncmp(ftp2ftype(fn2ftp(fn)),"ASCII",5))
470 /* Not ascii, add b to file mode */
471 if ((strchr(newmode,'b')==NULL) && (strchr(newmode,'B')==NULL))
478 #ifdef GMX_THREAD_MPI
479 tMPI_Lock_init(&(fio->mtx));
481 bRead = (newmode[0]=='r' && newmode[1]!='+');
482 bReadWrite = (newmode[1]=='+');
487 fio->iFTP = fn2ftp(fn);
488 fio->fn = strdup(fn);
491 /* If this file type is in the list of XDR files, open it like that */
492 if (in_ftpset(fio->iFTP,asize(ftpXDR),ftpXDR))
494 /* First check whether we have to make a backup,
495 * only for writing, not for read or append.
500 /* only make backups for normal gromacs */
506 /* Check whether file exists */
513 fio->fp = ffopen(fn,newmode);
515 /* determine the XDR direction */
516 if (newmode[0] == 'w' || newmode[0]=='a')
518 fio->xdrmode=XDR_ENCODE;
522 fio->xdrmode=XDR_DECODE;
526 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
530 /* If it is not, open it as a regular file */
531 fio->fp = ffopen(fn,newmode);
534 /* for appending seek to end of file to make sure ftell gives correct position
535 * important for checkpointing */
538 gmx_fseek(fio->fp, 0, SEEK_END);
543 /* Use stdin/stdout for I/O */
545 fio->fp = bRead ? stdin : stdout;
546 fio->fn = strdup("STDIO");
550 fio->bReadWrite = bReadWrite;
551 fio->bDouble= (sizeof(real) == sizeof(double));
554 fio->bLargerThan_off_t = FALSE;
556 /* set the reader/writer functions */
557 gmx_fio_set_iotype(fio);
559 /* and now insert this file into the list of open files. */
564 static int gmx_fio_close_locked(t_fileio *fio)
570 gmx_fatal(FARGS,"File %s closed twice!\n", fio->fn);
573 if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
575 xdr_destroy(fio->xdr);
579 /* Don't close stdin and stdout! */
580 if (!fio->bStdio && fio->fp!=NULL)
581 rc = ffclose(fio->fp); /* fclose returns 0 if happy */
588 int gmx_fio_close(t_fileio *fio)
592 #ifdef GMX_THREAD_MPI
593 /* first lock the big open_files mutex. */
594 /* We don't want two processes operating on the list at the same time */
595 tMPI_Thread_mutex_lock(&open_file_mutex);
599 /* first remove it from the list */
601 rc=gmx_fio_close_locked(fio);
606 #ifdef GMX_THREAD_MPI
607 tMPI_Thread_mutex_unlock(&open_file_mutex);
613 /* close only fp but keep FIO entry. */
614 int gmx_fio_fp_close(t_fileio *fio)
618 if (!in_ftpset(fio->iFTP,asize(ftpXDR),ftpXDR) && !fio->bStdio)
620 rc = ffclose(fio->fp); /* fclose returns 0 if happy */
628 FILE * gmx_fio_fopen(const char *fn, const char *mode)
633 fio = gmx_fio_open(fn, mode);
641 int gmx_fio_fclose(FILE *fp)
644 t_fileio *found=NULL;
647 cur=gmx_fio_get_first();
652 rc=gmx_fio_close_locked(cur);
654 gmx_fio_stop_getting_next(cur);
657 cur=gmx_fio_get_next(cur);
663 /* internal variant of get_file_md5 that operates on a locked file */
664 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
665 unsigned char digest[])
667 /*1MB: large size important to catch almost identical files */
668 #define CPT_CHK_LEN 1048576
670 unsigned char buf[CPT_CHK_LEN];
672 gmx_off_t seek_offset;
675 seek_offset = offset - CPT_CHK_LEN;
680 read_len = offset - seek_offset;
683 if (fio->fp && fio->bReadWrite)
685 ret=gmx_fseek(fio->fp, seek_offset, SEEK_SET);
688 gmx_fseek(fio->fp, 0, SEEK_END);
691 if (ret) /*either no fp, not readwrite, or fseek not successful */
696 /* the read puts the file position back to offset */
697 if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
699 /* not fatal: md5sum check to prevent overwriting files
700 * works (less safe) without
704 fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
707 else if (feof(fio->fp))
710 * For long runs that checkpoint frequently but write e.g. logs
711 * infrequently we don't want to issue lots of warnings before we
712 * have written anything to the log.
716 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
723 "\nTrying to get md5sum: Unknown reason for short read: %s\n",
727 gmx_fseek(fio->fp, 0, SEEK_END);
731 gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
732 it gives problems otherwise*/
736 fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
742 md5_append(&state, buf, read_len);
743 md5_finish(&state, digest);
754 * fio: file to compute md5 for
755 * offset: starting pointer of region to use for md5
756 * digest: return array of md5 sum
758 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
759 unsigned char digest[])
764 ret=gmx_fio_int_get_file_md5(fio, offset, digest);
770 /* The fio_mutex should ALWAYS be locked when this function is called */
771 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
775 /* Flush the file, so we are sure it is written */
776 if (gmx_fio_int_flush(fio))
781 "Cannot write file '%s'; maybe you are out of disk space?",
786 /* We cannot count on XDR being able to write 64-bit integers,
787 so separate into high/low 32-bit values.
788 In case the filesystem has 128-bit offsets we only care
789 about the first 64 bits - we'll have to fix
790 this when exabyte-size output files are common...
792 *offset=gmx_ftell(fio->fp);
797 int gmx_fio_check_file_position(t_fileio *fio)
799 /* If gmx_off_t is 4 bytes we can not store file offset > 2 GB.
800 * If we do not have ftello, we will play it safe.
802 #if (SIZEOF_GMX_OFF_T == 4 || !defined HAVE_FSEEKO)
806 gmx_fio_int_get_file_position(fio,&offset);
807 /* We have a 4 byte offset,
808 * make sure that we will detect out of range for all possible cases.
810 if (offset < 0 || offset > 2147483647)
812 fio->bLargerThan_off_t = TRUE;
820 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
823 int i, nfiles, rc, nalloc;
826 gmx_file_position_t * outputfiles;
832 /* pre-allocate 100 files */
834 snew(outputfiles,nalloc);
836 cur=gmx_fio_get_first();
839 /* Skip the checkpoint files themselves, since they could be open when
840 we call this routine... */
841 /* also skip debug files (shoud be the only iFTP==efNR) */
845 cur->iFTP != efCPT &&
849 /* This is an output file currently open for writing, add it */
850 if (nfiles == nalloc)
853 srenew(outputfiles,nalloc);
856 strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
858 /* Get the file position */
859 if (cur->bLargerThan_off_t)
861 /* -1 signals out of range */
862 outputfiles[nfiles].offset = -1;
863 outputfiles[nfiles].chksum_size = -1;
867 gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
869 outputfiles[nfiles].chksum_size
870 = gmx_fio_int_get_file_md5(cur,
871 outputfiles[nfiles].offset,
872 outputfiles[nfiles].chksum);
879 cur=gmx_fio_get_next(cur);
882 *p_outputfiles = outputfiles;
888 void gmx_fio_checktype(t_fileio *fio)
890 if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
894 else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
898 else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
903 gmx_fatal(FARGS, "Can not read/write topologies to file type %s",
909 void gmx_fio_setprecision(t_fileio *fio, gmx_bool bDouble)
912 fio->bDouble = bDouble;
916 gmx_bool gmx_fio_getdebug(t_fileio *fio)
927 void gmx_fio_setdebug(t_fileio *fio, gmx_bool bDebug)
930 fio->bDebug = bDebug;
934 char *gmx_fio_getname(t_fileio *fio)
944 int gmx_fio_getftp(t_fileio* fio)
955 void gmx_fio_rewind(t_fileio* fio)
961 xdr_destroy(fio->xdr);
963 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
973 int gmx_fio_flush(t_fileio* fio)
978 ret=gmx_fio_int_flush(fio);
986 static int gmx_fio_int_fsync(t_fileio *fio)
994 rc=gmx_fsync(fio->fp);
996 else if (fio->xdr) /* this should normally not happen */
998 rc=gmx_fsync((FILE*) fio->xdr->x_private);
999 /* ^ is this actually OK? */
1006 int gmx_fio_fsync(t_fileio *fio)
1011 rc=gmx_fio_int_fsync(fio);
1012 gmx_fio_unlock(fio);
1019 t_fileio *gmx_fio_all_output_fsync(void)
1024 cur=gmx_fio_get_first();
1027 /* skip debug files (shoud be the only iFTP==efNR) */
1033 /* if any of them fails, return failure code */
1034 int rc=gmx_fio_int_fsync(cur);
1035 if (rc != 0 && !ret)
1040 cur=gmx_fio_get_next(cur);
1043 /* in addition, we force these to be written out too, if they're being
1044 redirected. We don't check for errors because errors most likely mean
1045 that they're not redirected. */
1048 #if (defined(HAVE_FSYNC))
1049 /* again, fahcore defines HAVE_FSYNC and fsync() */
1050 fsync(STDOUT_FILENO);
1051 fsync(STDERR_FILENO);
1058 gmx_off_t gmx_fio_ftell(t_fileio* fio)
1064 ret = gmx_ftell(fio->fp);
1065 gmx_fio_unlock(fio);
1069 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
1076 rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
1083 gmx_fio_unlock(fio);
1087 FILE *gmx_fio_getfp(t_fileio *fio)
1094 gmx_fio_unlock(fio);
1098 XDR *gmx_fio_getxdr(t_fileio* fio)
1105 gmx_fio_unlock(fio);
1110 gmx_bool gmx_fio_getread(t_fileio* fio)
1116 gmx_fio_unlock(fio);
1121 int xtc_seek_frame(t_fileio *fio, int frame, int natoms)
1126 ret=xdr_xtc_seek_frame(frame, fio->fp, fio->xdr, natoms);
1127 gmx_fio_unlock(fio);
1132 int xtc_seek_time(t_fileio *fio, real time, int natoms,gmx_bool bSeekForwardOnly)
1137 ret=xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
1138 gmx_fio_unlock(fio);