Work around memory leak in t_state
[gromacs.git] / src / gromacs / fileio / gmxfio.cpp
blobe632956a8dcdb9adc0cdd7305a7c86368715683a
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, 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.
37 #include "gmxpre.h"
39 #include "gmxfio.h"
41 #include "config.h"
43 #include <cerrno>
44 #include <cstdio>
45 #include <cstring>
47 #if HAVE_IO_H
48 #include <io.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
54 #include "thread_mpi/threads.h"
56 #include "gromacs/fileio/filetypes.h"
57 #include "gromacs/fileio/md5.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/futil.h"
60 #include "gromacs/utility/smalloc.h"
62 #include "gmxfio-impl.h"
64 /* This is the new improved and thread safe version of gmxfio. */
68 /* the list of open files is a linked list, with a dummy element at its head;
69 it is initialized when the first file is opened. */
70 static t_fileio *open_files = NULL;
73 /* this mutex locks the open_files structure so that no two threads can
74 modify it.
76 For now, we use this as a coarse grained lock on all file
77 insertion/deletion operations because it makes avoiding deadlocks
78 easier, and adds almost no overhead: the only overhead is during
79 opening and closing of files, or during global operations like
80 iterating along all open files. All these cases should be rare
81 during the simulation. */
82 static tMPI_Thread_mutex_t open_file_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
84 /******************************************************************
86 * Internal functions:
88 ******************************************************************/
90 static int gmx_fio_int_flush(t_fileio* fio)
92 int rc = 0;
94 if (fio->fp)
96 rc = fflush(fio->fp);
99 return rc;
102 /* lock the mutex associated with this fio. This needs to be done for every
103 type of access to the fio's elements. */
104 void gmx_fio_lock(t_fileio *fio)
106 tMPI_Lock_lock(&(fio->mtx));
108 /* unlock the mutex associated with this fio. */
109 void gmx_fio_unlock(t_fileio *fio)
111 tMPI_Lock_unlock(&(fio->mtx));
114 /* make a dummy head element, assuming we locked everything. */
115 static void gmx_fio_make_dummy(void)
117 if (!open_files)
119 snew(open_files, 1);
120 open_files->fp = NULL;
121 open_files->fn = NULL;
122 open_files->next = open_files;
123 open_files->prev = open_files;
124 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 /* first lock the big open_files mutex. */
146 tMPI_Thread_mutex_lock(&open_file_mutex);
147 /* now check whether the dummy element has been allocated,
148 and allocate it if it hasn't */
149 gmx_fio_make_dummy();
151 /* and lock the fio we got and the list's head **/
152 gmx_fio_lock(fio);
153 gmx_fio_lock(open_files);
154 prev = open_files->prev;
155 /* lock the element after the current one */
156 if (prev != open_files)
158 gmx_fio_lock(prev);
161 /* now do the actual insertion: */
162 fio->next = open_files;
163 open_files->prev = fio;
164 prev->next = fio;
165 fio->prev = prev;
167 /* now unlock all our locks */
168 if (prev != open_files)
170 gmx_fio_unlock(prev);
172 gmx_fio_unlock(open_files);
173 gmx_fio_unlock(fio);
175 /* now unlock the big open_files mutex. */
176 tMPI_Thread_mutex_unlock(&open_file_mutex);
179 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
180 it locked.
181 NOTE: We also assume that the open_file_mutex has been locked */
182 static void gmx_fio_remove(t_fileio *fio)
184 /* lock prev, because we're changing it */
185 gmx_fio_lock(fio->prev);
187 /* now set the prev's pointer */
188 fio->prev->next = fio->next;
189 gmx_fio_unlock(fio->prev);
191 /* with the next ptr, we can simply lock while the original was locked */
192 gmx_fio_lock(fio->next);
193 fio->next->prev = fio->prev;
194 gmx_fio_unlock(fio->next);
196 /* and make sure we point nowhere in particular */
197 fio->next = fio->prev = fio;
201 /* get the first open file, or NULL if there is none.
202 Returns a locked fio. */
203 static t_fileio *gmx_fio_get_first(void)
205 t_fileio *ret;
206 /* first lock the big open_files mutex and the dummy's mutex */
208 /* first lock the big open_files mutex. */
209 tMPI_Thread_mutex_lock(&open_file_mutex);
210 gmx_fio_make_dummy();
212 gmx_fio_lock(open_files);
213 ret = open_files->next;
216 /* check whether there were any to begin with */
217 if (ret == open_files)
219 /* after this, the open_file pointer should never change */
220 ret = NULL;
222 else
224 gmx_fio_lock(open_files->next);
226 gmx_fio_unlock(open_files);
229 return ret;
232 /* get the next open file, or NULL if there is none.
233 Unlocks the previous fio and locks the next one. */
234 static t_fileio *gmx_fio_get_next(t_fileio *fio)
236 t_fileio *ret;
238 ret = fio->next;
239 /* check if that was the last one */
240 if (fio->next == open_files)
242 ret = NULL;
243 tMPI_Thread_mutex_unlock(&open_file_mutex);
245 else
247 gmx_fio_lock(ret);
249 gmx_fio_unlock(fio);
251 return ret;
254 /* Stop looping through the open_files. Unlocks the global lock. */
255 static void gmx_fio_stop_getting_next(t_fileio *fio)
257 gmx_fio_unlock(fio);
258 tMPI_Thread_mutex_unlock(&open_file_mutex);
264 /*****************************************************************
266 * EXPORTED SECTION
268 *****************************************************************/
269 t_fileio *gmx_fio_open(const char *fn, const char *mode)
271 t_fileio *fio = NULL;
272 char newmode[5];
273 gmx_bool bRead, bReadWrite;
275 /* sanitize the mode string */
276 if (std::strncmp(mode, "r+", 2) == 0)
278 std::strcpy(newmode, "r+");
280 else if (mode[0] == 'r')
282 std::strcpy(newmode, "r");
284 else if (strncmp(mode, "w+", 2) == 0)
286 std::strcpy(newmode, "w+");
288 else if (mode[0] == 'w')
290 std::strcpy(newmode, "w");
292 else if (strncmp(mode, "a+", 2) == 0)
294 std::strcpy(newmode, "a+");
296 else if (mode[0] == 'a')
298 std::strcpy(newmode, "a");
300 else
302 gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
305 /* Check if it should be opened as a binary file */
306 if (!ftp_is_text(fn2ftp(fn)))
308 strcat(newmode, "b");
311 snew(fio, 1);
312 tMPI_Lock_init(&(fio->mtx));
313 bRead = (newmode[0] == 'r' && newmode[1] != '+');
314 bReadWrite = (newmode[1] == '+');
315 fio->fp = NULL;
316 fio->xdr = NULL;
317 if (fn)
319 if (fn2ftp(fn) == efTNG)
321 gmx_incons("gmx_fio_open may not be used to open TNG files");
323 fio->iFTP = fn2ftp(fn);
324 fio->fn = gmx_strdup(fn);
326 fio->fp = gmx_ffopen(fn, newmode);
327 /* If this file type is in the list of XDR files, open it like that */
328 if (ftp_is_xdr(fio->iFTP))
330 /* determine the XDR direction */
331 if (newmode[0] == 'w' || newmode[0] == 'a')
333 fio->xdrmode = XDR_ENCODE;
335 else
337 fio->xdrmode = XDR_DECODE;
339 snew(fio->xdr, 1);
340 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
343 /* for appending seek to end of file to make sure ftell gives correct position
344 * important for checkpointing */
345 if (newmode[0] == 'a')
347 gmx_fseek(fio->fp, 0, SEEK_END);
350 else
352 gmx_fatal(FARGS, "Cannot open file with NULL filename string");
355 fio->bRead = bRead;
356 fio->bReadWrite = bReadWrite;
357 fio->bDouble = (sizeof(real) == sizeof(double));
359 /* and now insert this file into the list of open files. */
360 gmx_fio_insert(fio);
361 return fio;
364 static int gmx_fio_close_locked(t_fileio *fio)
366 int rc = 0;
368 if (fio->xdr != NULL)
370 xdr_destroy(fio->xdr);
371 sfree(fio->xdr);
374 if (fio->fp != NULL)
376 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
380 return rc;
383 int gmx_fio_close(t_fileio *fio)
385 int rc = 0;
387 /* first lock the big open_files mutex. */
388 /* We don't want two processes operating on the list at the same time */
389 tMPI_Thread_mutex_lock(&open_file_mutex);
391 gmx_fio_lock(fio);
392 /* first remove it from the list */
393 gmx_fio_remove(fio);
394 rc = gmx_fio_close_locked(fio);
395 gmx_fio_unlock(fio);
397 sfree(fio->fn);
398 sfree(fio);
400 tMPI_Thread_mutex_unlock(&open_file_mutex);
402 return rc;
405 /* close only fp but keep FIO entry. */
406 int gmx_fio_fp_close(t_fileio *fio)
408 int rc = 0;
409 gmx_fio_lock(fio);
410 if (fio->xdr == NULL)
412 rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
413 fio->fp = NULL;
415 gmx_fio_unlock(fio);
417 return rc;
420 FILE * gmx_fio_fopen(const char *fn, const char *mode)
422 FILE *ret;
423 t_fileio *fio;
425 fio = gmx_fio_open(fn, mode);
426 gmx_fio_lock(fio);
427 ret = fio->fp;
428 gmx_fio_unlock(fio);
430 return ret;
433 int gmx_fio_fclose(FILE *fp)
435 t_fileio *cur;
436 int rc = -1;
438 cur = gmx_fio_get_first();
439 while (cur)
441 if (cur->fp == fp)
443 rc = gmx_fio_close_locked(cur);
444 gmx_fio_remove(cur);
445 gmx_fio_stop_getting_next(cur);
446 sfree(cur->fn);
447 sfree(cur);
448 break;
450 cur = gmx_fio_get_next(cur);
453 return rc;
456 /* internal variant of get_file_md5 that operates on a locked file */
457 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
458 unsigned char digest[])
460 /*1MB: large size important to catch almost identical files */
461 #define CPT_CHK_LEN 1048576
462 md5_state_t state;
463 unsigned char *buf;
464 gmx_off_t read_len;
465 gmx_off_t seek_offset;
466 int ret = -1;
468 seek_offset = offset - CPT_CHK_LEN;
469 if (seek_offset < 0)
471 seek_offset = 0;
473 read_len = offset - seek_offset;
476 if (fio->fp && fio->bReadWrite)
478 ret = gmx_fseek(fio->fp, seek_offset, SEEK_SET);
479 if (ret)
481 gmx_fseek(fio->fp, 0, SEEK_END);
484 if (ret) /*either no fp, not readwrite, or fseek not successful */
486 return -1;
489 snew(buf, CPT_CHK_LEN);
490 /* the read puts the file position back to offset */
491 if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
493 /* not fatal: md5sum check to prevent overwriting files
494 * works (less safe) without
495 * */
496 if (ferror(fio->fp))
498 fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
499 strerror(errno));
501 else if (feof(fio->fp))
504 * For long runs that checkpoint frequently but write e.g. logs
505 * infrequently we don't want to issue lots of warnings before we
506 * have written anything to the log.
508 if (0)
510 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
513 else
515 fprintf(
516 stderr,
517 "\nTrying to get md5sum: Unknown reason for short read: %s\n",
518 fio->fn);
521 gmx_fseek(fio->fp, 0, SEEK_END);
523 ret = -1;
525 gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
526 it gives problems otherwise*/
528 if (debug)
530 fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
533 if (!ret)
535 gmx_md5_init(&state);
536 gmx_md5_append(&state, buf, read_len);
537 gmx_md5_finish(&state, digest);
538 ret = read_len;
540 sfree(buf);
541 return ret;
546 * fio: file to compute md5 for
547 * offset: starting pointer of region to use for md5
548 * digest: return array of md5 sum
550 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
551 unsigned char digest[])
553 int ret;
555 gmx_fio_lock(fio);
556 ret = gmx_fio_int_get_file_md5(fio, offset, digest);
557 gmx_fio_unlock(fio);
559 return ret;
562 /* The fio_mutex should ALWAYS be locked when this function is called */
563 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
565 /* Flush the file, so we are sure it is written */
566 if (gmx_fio_int_flush(fio))
568 char buf[STRLEN];
569 sprintf(
570 buf,
571 "Cannot write file '%s'; maybe you are out of disk space?",
572 fio->fn);
573 gmx_file(buf);
576 /* We cannot count on XDR being able to write 64-bit integers,
577 so separate into high/low 32-bit values.
578 In case the filesystem has 128-bit offsets we only care
579 about the first 64 bits - we'll have to fix
580 this when exabyte-size output files are common...
582 *offset = gmx_ftell(fio->fp);
584 return 0;
587 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
588 int *p_nfiles)
590 int nfiles, nalloc;
591 gmx_file_position_t * outputfiles;
592 t_fileio *cur;
594 nfiles = 0;
596 /* pre-allocate 100 files */
597 nalloc = 100;
598 snew(outputfiles, nalloc);
600 cur = gmx_fio_get_first();
601 while (cur)
603 /* Skip the checkpoint files themselves, since they could be open when
604 we call this routine... */
605 if (!cur->bRead && cur->iFTP != efCPT)
607 /* This is an output file currently open for writing, add it */
608 if (nfiles == nalloc)
610 nalloc += 100;
611 srenew(outputfiles, nalloc);
614 std::strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
616 /* Get the file position */
617 gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
618 #ifndef GMX_FAHCORE
619 outputfiles[nfiles].chksum_size
620 = gmx_fio_int_get_file_md5(cur,
621 outputfiles[nfiles].offset,
622 outputfiles[nfiles].chksum);
623 #endif
624 nfiles++;
627 cur = gmx_fio_get_next(cur);
629 *p_nfiles = nfiles;
630 *p_outputfiles = outputfiles;
632 return 0;
636 char *gmx_fio_getname(t_fileio *fio)
638 char *ret;
639 gmx_fio_lock(fio);
640 ret = fio->fn;
641 gmx_fio_unlock(fio);
643 return ret;
646 int gmx_fio_getftp(t_fileio* fio)
648 int ret;
650 gmx_fio_lock(fio);
651 ret = fio->iFTP;
652 gmx_fio_unlock(fio);
654 return ret;
657 void gmx_fio_rewind(t_fileio* fio)
659 gmx_fio_lock(fio);
661 if (fio->xdr)
663 xdr_destroy(fio->xdr);
664 frewind(fio->fp);
665 xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
667 else
669 frewind(fio->fp);
671 gmx_fio_unlock(fio);
675 int gmx_fio_flush(t_fileio* fio)
677 int ret;
679 gmx_fio_lock(fio);
680 ret = gmx_fio_int_flush(fio);
681 gmx_fio_unlock(fio);
683 return ret;
688 static int gmx_fio_int_fsync(t_fileio *fio)
690 int rc = 0;
692 if (fio->fp)
694 rc = gmx_fsync(fio->fp);
696 return rc;
700 int gmx_fio_fsync(t_fileio *fio)
702 int rc;
704 gmx_fio_lock(fio);
705 rc = gmx_fio_int_fsync(fio);
706 gmx_fio_unlock(fio);
708 return rc;
713 t_fileio *gmx_fio_all_output_fsync(void)
715 t_fileio *ret = NULL;
716 t_fileio *cur;
718 cur = gmx_fio_get_first();
719 while (cur)
721 if (!cur->bRead)
723 /* if any of them fails, return failure code */
724 int rc = gmx_fio_int_fsync(cur);
725 if (rc != 0 && !ret)
727 ret = cur;
730 cur = gmx_fio_get_next(cur);
733 /* in addition, we force these to be written out too, if they're being
734 redirected. We don't check for errors because errors most likely mean
735 that they're not redirected. */
736 fflush(stdout);
737 fflush(stderr);
738 #if HAVE_FSYNC
739 /* again, fahcore defines HAVE_FSYNC and fsync() */
740 fsync(STDOUT_FILENO);
741 fsync(STDERR_FILENO);
742 #endif
744 return ret;
748 gmx_off_t gmx_fio_ftell(t_fileio* fio)
750 gmx_off_t ret = 0;
752 gmx_fio_lock(fio);
753 if (fio->fp)
755 ret = gmx_ftell(fio->fp);
757 gmx_fio_unlock(fio);
758 return ret;
761 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
763 int rc;
765 gmx_fio_lock(fio);
766 if (fio->fp)
768 rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
770 else
772 gmx_file(fio->fn);
773 rc = -1;
775 gmx_fio_unlock(fio);
776 return rc;
779 FILE *gmx_fio_getfp(t_fileio *fio)
781 FILE *ret = NULL;
783 gmx_fio_lock(fio);
784 if (fio->fp)
786 ret = fio->fp;
788 gmx_fio_unlock(fio);
789 return ret;
792 gmx_bool gmx_fio_getread(t_fileio* fio)
794 gmx_bool ret;
796 gmx_fio_lock(fio);
797 ret = fio->bRead;
798 gmx_fio_unlock(fio);
800 return ret;
803 int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
805 int ret;
807 gmx_fio_lock(fio);
808 ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
809 gmx_fio_unlock(fio);
811 return ret;