Further gmxfio cleanup related to xdr handling
[alexxy/gromacs.git] / src / gromacs / fileio / gmxfio.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "gmxfio.h"
40
41 #include "config.h"
42
43 #include <errno.h>
44 #include <stdio.h>
45 #include <string.h>
46
47 #ifdef HAVE_IO_H
48 #include <io.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53
54 #include "thread_mpi/threads.h"
55
56 #include "gromacs/fileio/filenm.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"
61
62 #include "gmxfio-impl.h"
63
64 /* This is the new improved and thread safe version of gmxfio. */
65
66
67
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;
71
72
73 /* this mutex locks the open_files structure so that no two threads can
74    modify it.
75
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;
83
84 const char                *eioNames[eioNR] =
85 {
86     "REAL", "INT", "GMX_STE_T", "UCHAR", "NUCHAR", "USHORT", "RVEC", "NRVEC",
87     "IVEC", "STRING"
88 };
89
90 /******************************************************************
91  *
92  * Internal functions:
93  *
94  ******************************************************************/
95
96 static int gmx_fio_int_flush(t_fileio* fio)
97 {
98     int rc = 0;
99
100     if (fio->fp)
101     {
102         rc = fflush(fio->fp);
103     }
104     else if (fio->xdr)
105     {
106         rc = fflush((FILE *) fio->xdr->x_private);
107     }
108
109     return rc;
110 }
111
112
113 /* check the number of items given against the type */
114 void gmx_fio_check_nitem(int eio, int nitem, const char *file, int line)
115 {
116     if ((nitem != 1) && !((eio == eioNRVEC) || (eio == eioNUCHAR)))
117     {
118         gmx_fatal(FARGS,
119                   "nitem (%d) may differ from 1 only for %s or %s, not   for %s"
120                   "(%s, %d)", nitem, eioNames[eioNUCHAR], eioNames[eioNRVEC],
121                   eioNames[eio], file, line);
122     }
123 }
124
125
126 /* output a data type error. */
127 void gmx_fio_fe(t_fileio *fio, int eio, const char *desc,
128                 const char *srcfile, int line)
129 {
130
131     gmx_fatal(FARGS, "Trying to %s %s type %d (%s), src %s, line %d",
132               fio->bRead ? "read" : "write", desc, eio,
133               ((eio >= 0) && (eio < eioNR)) ? eioNames[eio] : "unknown",
134               srcfile, line);
135 }
136
137 /* lock the mutex associated with this fio. This needs to be done for every
138    type of access to the fio's elements. */
139 void gmx_fio_lock(t_fileio *fio)
140 {
141     tMPI_Lock_lock(&(fio->mtx));
142 }
143 /* unlock the mutex associated with this fio.  */
144 void gmx_fio_unlock(t_fileio *fio)
145 {
146     tMPI_Lock_unlock(&(fio->mtx));
147 }
148
149 /* make a dummy head element, assuming we locked everything. */
150 static void gmx_fio_make_dummy(void)
151 {
152     if (!open_files)
153     {
154         snew(open_files, 1);
155         open_files->fp   = NULL;
156         open_files->fn   = NULL;
157         open_files->next = open_files;
158         open_files->prev = open_files;
159         tMPI_Lock_init(&(open_files->mtx));
160     }
161 }
162
163
164
165
166
167
168
169 /***********************************************************************
170  *
171  * FILE LIST OPERATIONS
172  *
173  ***********************************************************************/
174
175
176 /* insert a new t_fileio into the list */
177 static void gmx_fio_insert(t_fileio *fio)
178 {
179     t_fileio *prev;
180     /* first lock the big open_files mutex. */
181     tMPI_Thread_mutex_lock(&open_file_mutex);
182     /* now check whether the dummy element has been allocated,
183        and allocate it if it hasn't */
184     gmx_fio_make_dummy();
185
186     /* and lock the fio we got and the list's head **/
187     gmx_fio_lock(fio);
188     gmx_fio_lock(open_files);
189     prev = open_files->prev;
190     /* lock the element after the current one */
191     if (prev != open_files)
192     {
193         gmx_fio_lock(prev);
194     }
195
196     /* now do the actual insertion: */
197     fio->next        = open_files;
198     open_files->prev = fio;
199     prev->next       = fio;
200     fio->prev        = prev;
201
202     /* now unlock all our locks */
203     if (prev != open_files)
204     {
205         gmx_fio_unlock(prev);
206     }
207     gmx_fio_unlock(open_files);
208     gmx_fio_unlock(fio);
209
210     /* now unlock the big open_files mutex.  */
211     tMPI_Thread_mutex_unlock(&open_file_mutex);
212 }
213
214 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
215    it locked.
216    NOTE: We also assume that the open_file_mutex has been locked */
217 static void gmx_fio_remove(t_fileio *fio)
218 {
219     t_fileio *prev;
220
221     /* lock prev, because we're changing it */
222     gmx_fio_lock(fio->prev);
223
224     /* now set the prev's pointer */
225     fio->prev->next = fio->next;
226     gmx_fio_unlock(fio->prev);
227
228     /* with the next ptr, we can simply lock while the original was locked */
229     gmx_fio_lock(fio->next);
230     fio->next->prev = fio->prev;
231     gmx_fio_unlock(fio->next);
232
233     /* and make sure we point nowhere in particular */
234     fio->next = fio->prev = fio;
235 }
236
237
238 /* get the first open file, or NULL if there is none.
239    Returns a locked fio. */
240 static t_fileio *gmx_fio_get_first(void)
241 {
242     t_fileio *ret;
243     /* first lock the big open_files mutex and the dummy's mutex */
244
245     /* first lock the big open_files mutex. */
246     tMPI_Thread_mutex_lock(&open_file_mutex);
247     gmx_fio_make_dummy();
248
249     gmx_fio_lock(open_files);
250     ret = open_files->next;
251
252
253     /* check whether there were any to begin with */
254     if (ret == open_files)
255     {
256         /* after this, the open_file pointer should never change */
257         ret = NULL;
258     }
259     else
260     {
261         gmx_fio_lock(open_files->next);
262     }
263     gmx_fio_unlock(open_files);
264
265
266     return ret;
267 }
268
269 /* get the next open file, or NULL if there is none.
270    Unlocks the previous fio and locks the next one. */
271 static t_fileio *gmx_fio_get_next(t_fileio *fio)
272 {
273     t_fileio *ret;
274
275     ret = fio->next;
276     /* check if that was the last one */
277     if (fio->next == open_files)
278     {
279         ret = NULL;
280         tMPI_Thread_mutex_unlock(&open_file_mutex);
281     }
282     else
283     {
284         gmx_fio_lock(ret);
285     }
286     gmx_fio_unlock(fio);
287
288     return ret;
289 }
290
291 /* Stop looping through the open_files.  Unlocks the global lock. */
292 static void gmx_fio_stop_getting_next(t_fileio *fio)
293 {
294     gmx_fio_unlock(fio);
295     tMPI_Thread_mutex_unlock(&open_file_mutex);
296 }
297
298
299
300
301 /*****************************************************************
302  *
303  *                     EXPORTED SECTION
304  *
305  *****************************************************************/
306 t_fileio *gmx_fio_open(const char *fn, const char *mode)
307 {
308     t_fileio *fio = NULL;
309     int       i;
310     char      newmode[5];
311     gmx_bool  bRead, bReadWrite;
312     int       xdrid;
313
314     /* sanitize the mode string */
315     if (strncmp(mode, "r+", 2) == 0)
316     {
317         strcpy(newmode, "r+");
318     }
319     else if (mode[0] == 'r')
320     {
321         strcpy(newmode, "r");
322     }
323     else if (strncmp(mode, "w+", 2) == 0)
324     {
325         strcpy(newmode, "w+");
326     }
327     else if (mode[0] == 'w')
328     {
329         strcpy(newmode, "w");
330     }
331     else if (strncmp(mode, "a+", 2) == 0)
332     {
333         strcpy(newmode, "a+");
334     }
335     else if (mode[0] == 'a')
336     {
337         strcpy(newmode, "a");
338     }
339     else
340     {
341         gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
342     }
343
344     /* Check if it should be opened as a binary file */
345     if (!ftp_is_text(fn2ftp(fn)))
346     {
347         /* Not ascii, add b to file mode */
348         if ((strchr(newmode, 'b') == NULL) && (strchr(newmode, 'B') == NULL))
349         {
350             strcat(newmode, "b");
351         }
352     }
353
354     snew(fio, 1);
355     tMPI_Lock_init(&(fio->mtx));
356     bRead      = (newmode[0] == 'r' && newmode[1] != '+');
357     bReadWrite = (newmode[1] == '+');
358     fio->fp    = NULL;
359     fio->xdr   = NULL;
360     if (fn)
361     {
362         if (fn2ftp(fn) == efTNG)
363         {
364             gmx_incons("gmx_fio_open may not be used to open TNG files");
365         }
366         fio->iFTP   = fn2ftp(fn);
367         fio->fn     = gmx_strdup(fn);
368
369         /* If this file type is in the list of XDR files, open it like that */
370         if (ftp_is_xdr(fio->iFTP))
371         {
372             /* First check whether we have to make a backup,
373              * only for writing, not for read or append.
374              */
375             if (newmode[0] == 'w')
376             {
377 #ifndef GMX_FAHCORE
378                 /* only make backups for normal gromacs */
379                 make_backup(fn);
380 #endif
381             }
382             else
383             {
384                 /* Check whether file exists */
385                 if (!gmx_fexist(fn))
386                 {
387                     gmx_open(fn);
388                 }
389             }
390             /* Open the file */
391             fio->fp = gmx_ffopen(fn, newmode);
392
393             /* determine the XDR direction */
394             if (newmode[0] == 'w' || newmode[0] == 'a')
395             {
396                 fio->xdrmode = XDR_ENCODE;
397             }
398             else
399             {
400                 fio->xdrmode = XDR_DECODE;
401             }
402
403             snew(fio->xdr, 1);
404             xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
405         }
406         else
407         {
408             /* If it is not, open it as a regular file */
409             fio->fp = gmx_ffopen(fn, newmode);
410         }
411
412         /* for appending seek to end of file to make sure ftell gives correct position
413          * important for checkpointing */
414         if (newmode[0] == 'a')
415         {
416             gmx_fseek(fio->fp, 0, SEEK_END);
417         }
418     }
419     fio->bRead             = bRead;
420     fio->bReadWrite        = bReadWrite;
421     fio->bDouble           = (sizeof(real) == sizeof(double));
422     fio->bDebug            = FALSE;
423     fio->bOpen             = TRUE;
424
425     /* and now insert this file into the list of open files. */
426     gmx_fio_insert(fio);
427     return fio;
428 }
429
430 static int gmx_fio_close_locked(t_fileio *fio)
431 {
432     int rc = 0;
433
434     if (!fio->bOpen)
435     {
436         gmx_fatal(FARGS, "File %s closed twice!\n", fio->fn);
437     }
438
439     if (ftp_is_xdr(fio->iFTP))
440     {
441         xdr_destroy(fio->xdr);
442         sfree(fio->xdr);
443     }
444
445     if (fio->fp != NULL)
446     {
447         rc = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
448
449     }
450     fio->bOpen = FALSE;
451
452     return rc;
453 }
454
455 int gmx_fio_close(t_fileio *fio)
456 {
457     int rc = 0;
458
459     /* first lock the big open_files mutex. */
460     /* We don't want two processes operating on the list at the same time */
461     tMPI_Thread_mutex_lock(&open_file_mutex);
462
463     if (fio->iFTP == efTNG)
464     {
465         gmx_incons("gmx_fio_close should not be called on a TNG file");
466     }
467     gmx_fio_lock(fio);
468     /* first remove it from the list */
469     gmx_fio_remove(fio);
470     rc = gmx_fio_close_locked(fio);
471     gmx_fio_unlock(fio);
472
473     sfree(fio->fn);
474     sfree(fio);
475
476     tMPI_Thread_mutex_unlock(&open_file_mutex);
477
478     return rc;
479 }
480
481 /* close only fp but keep FIO entry. */
482 int gmx_fio_fp_close(t_fileio *fio)
483 {
484     int rc = 0;
485     gmx_fio_lock(fio);
486     if (!ftp_is_xdr(fio->iFTP))
487     {
488         rc      = gmx_ffclose(fio->fp); /* fclose returns 0 if happy */
489         fio->fp = NULL;
490     }
491     gmx_fio_unlock(fio);
492
493     return rc;
494 }
495
496 FILE * gmx_fio_fopen(const char *fn, const char *mode)
497 {
498     FILE     *fp, *ret;
499     t_fileio *fio;
500
501     fio = gmx_fio_open(fn, mode);
502     gmx_fio_lock(fio);
503     ret = fio->fp;
504     gmx_fio_unlock(fio);
505
506     return ret;
507 }
508
509 int gmx_fio_fclose(FILE *fp)
510 {
511     t_fileio *cur;
512     t_fileio *found = NULL;
513     int       rc    = -1;
514
515     cur = gmx_fio_get_first();
516     while (cur)
517     {
518         if (cur->fp == fp)
519         {
520             rc = gmx_fio_close_locked(cur);
521             gmx_fio_remove(cur);
522             gmx_fio_stop_getting_next(cur);
523             sfree(cur->fn);
524             sfree(cur);
525             break;
526         }
527         cur = gmx_fio_get_next(cur);
528     }
529
530     return rc;
531 }
532
533 /* internal variant of get_file_md5 that operates on a locked file */
534 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
535                                     unsigned char digest[])
536 {
537     /*1MB: large size important to catch almost identical files */
538 #define CPT_CHK_LEN  1048576
539     md5_state_t    state;
540     unsigned char *buf;
541     gmx_off_t      read_len;
542     gmx_off_t      seek_offset;
543     int            ret = -1;
544
545     seek_offset = offset - CPT_CHK_LEN;
546     if (seek_offset < 0)
547     {
548         seek_offset = 0;
549     }
550     read_len = offset - seek_offset;
551
552
553     if (fio->fp && fio->bReadWrite)
554     {
555         ret = gmx_fseek(fio->fp, seek_offset, SEEK_SET);
556         if (ret)
557         {
558             gmx_fseek(fio->fp, 0, SEEK_END);
559         }
560     }
561     if (ret) /*either no fp, not readwrite, or fseek not successful */
562     {
563         return -1;
564     }
565
566     snew(buf, CPT_CHK_LEN);
567     /* the read puts the file position back to offset */
568     if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
569     {
570         /* not fatal: md5sum check to prevent overwriting files
571          * works (less safe) without
572          * */
573         if (ferror(fio->fp))
574         {
575             fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
576                     strerror(errno));
577         }
578         else if (feof(fio->fp))
579         {
580             /*
581              * For long runs that checkpoint frequently but write e.g. logs
582              * infrequently we don't want to issue lots of warnings before we
583              * have written anything to the log.
584              */
585             if (0)
586             {
587                 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
588             }
589         }
590         else
591         {
592             fprintf(
593                     stderr,
594                     "\nTrying to get md5sum: Unknown reason for short read: %s\n",
595                     fio->fn);
596         }
597
598         gmx_fseek(fio->fp, 0, SEEK_END);
599
600         ret = -1;
601     }
602     gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
603                                         it gives problems otherwise*/
604
605     if (debug)
606     {
607         fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
608     }
609
610     if (!ret)
611     {
612         gmx_md5_init(&state);
613         gmx_md5_append(&state, buf, read_len);
614         gmx_md5_finish(&state, digest);
615         ret = read_len;
616     }
617     sfree(buf);
618     return ret;
619 }
620
621
622 /*
623  * fio: file to compute md5 for
624  * offset: starting pointer of region to use for md5
625  * digest: return array of md5 sum
626  */
627 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
628                          unsigned char digest[])
629 {
630     int ret;
631
632     gmx_fio_lock(fio);
633     ret = gmx_fio_int_get_file_md5(fio, offset, digest);
634     gmx_fio_unlock(fio);
635
636     return ret;
637 }
638
639 /* The fio_mutex should ALWAYS be locked when this function is called */
640 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
641 {
642     char buf[STRLEN];
643
644     /* Flush the file, so we are sure it is written */
645     if (gmx_fio_int_flush(fio))
646     {
647         char buf[STRLEN];
648         sprintf(
649                 buf,
650                 "Cannot write file '%s'; maybe you are out of disk space?",
651                 fio->fn);
652         gmx_file(buf);
653     }
654
655     /* We cannot count on XDR being able to write 64-bit integers,
656        so separate into high/low 32-bit values.
657        In case the filesystem has 128-bit offsets we only care
658        about the first 64 bits - we'll have to fix
659        this when exabyte-size output files are common...
660      */
661     *offset = gmx_ftell(fio->fp);
662
663     return 0;
664 }
665
666 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
667                                       int                  *p_nfiles)
668 {
669     int                   i, nfiles, rc, nalloc;
670     int                   pos_hi, pos_lo;
671     long                  pos;
672     gmx_file_position_t * outputfiles;
673     char                  buf[STRLEN];
674     t_fileio             *cur;
675
676     nfiles = 0;
677
678     /* pre-allocate 100 files */
679     nalloc = 100;
680     snew(outputfiles, nalloc);
681
682     cur = gmx_fio_get_first();
683     while (cur)
684     {
685         /* Skip the checkpoint files themselves, since they could be open when
686            we call this routine... */
687         if (cur->bOpen && !cur->bRead && cur->iFTP != efCPT)
688         {
689             int ret;
690             /* This is an output file currently open for writing, add it */
691             if (nfiles == nalloc)
692             {
693                 nalloc += 100;
694                 srenew(outputfiles, nalloc);
695             }
696
697             strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
698
699             /* Get the file position */
700             gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
701 #ifndef GMX_FAHCORE
702             outputfiles[nfiles].chksum_size
703                 = gmx_fio_int_get_file_md5(cur,
704                                            outputfiles[nfiles].offset,
705                                            outputfiles[nfiles].chksum);
706 #endif
707             nfiles++;
708         }
709
710         cur = gmx_fio_get_next(cur);
711     }
712     *p_nfiles      = nfiles;
713     *p_outputfiles = outputfiles;
714
715     return 0;
716 }
717
718
719 void gmx_fio_checktype(t_fileio *fio)
720 {
721     if (!ftp_is_xdr(fio->iFTP))
722     {
723         gmx_fatal(FARGS, "Can not read/write topologies to file type %s",
724                   ftp2ext(fio->iFTP));
725     }
726
727 }
728
729
730 void gmx_fio_setprecision(t_fileio *fio, gmx_bool bDouble)
731 {
732     gmx_fio_lock(fio);
733     fio->bDouble = bDouble;
734     gmx_fio_unlock(fio);
735 }
736
737 void gmx_fio_setdebug(t_fileio *fio, gmx_bool bDebug)
738 {
739     gmx_fio_lock(fio);
740     fio->bDebug = bDebug;
741     gmx_fio_unlock(fio);
742 }
743
744 char *gmx_fio_getname(t_fileio *fio)
745 {
746     char *ret;
747     gmx_fio_lock(fio);
748     ret = fio->fn;
749     gmx_fio_unlock(fio);
750
751     return ret;
752 }
753
754 int gmx_fio_getftp(t_fileio* fio)
755 {
756     int ret;
757
758     gmx_fio_lock(fio);
759     ret = fio->iFTP;
760     gmx_fio_unlock(fio);
761
762     return ret;
763 }
764
765 void gmx_fio_rewind(t_fileio* fio)
766 {
767     gmx_fio_lock(fio);
768
769     if (fio->xdr)
770     {
771         xdr_destroy(fio->xdr);
772         frewind(fio->fp);
773         xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
774     }
775     else
776     {
777         frewind(fio->fp);
778     }
779     gmx_fio_unlock(fio);
780 }
781
782
783 int gmx_fio_flush(t_fileio* fio)
784 {
785     int ret;
786
787     gmx_fio_lock(fio);
788     ret = gmx_fio_int_flush(fio);
789     gmx_fio_unlock(fio);
790
791     return ret;
792 }
793
794
795
796 static int gmx_fio_int_fsync(t_fileio *fio)
797 {
798     int rc    = 0;
799     int filen = -1;
800
801
802     if (fio->fp)
803     {
804         rc = gmx_fsync(fio->fp);
805     }
806     else if (fio->xdr) /* this should normally not happen */
807     {
808         rc = gmx_fsync((FILE*) fio->xdr->x_private);
809         /* ^ is this actually OK? */
810     }
811
812     return rc;
813 }
814
815
816 int gmx_fio_fsync(t_fileio *fio)
817 {
818     int rc;
819
820     gmx_fio_lock(fio);
821     rc = gmx_fio_int_fsync(fio);
822     gmx_fio_unlock(fio);
823
824     return rc;
825 }
826
827
828
829 t_fileio *gmx_fio_all_output_fsync(void)
830 {
831     t_fileio *ret = NULL;
832     t_fileio *cur;
833
834     cur = gmx_fio_get_first();
835     while (cur)
836     {
837         if (cur->bOpen && !cur->bRead)
838         {
839             /* if any of them fails, return failure code */
840             int rc = gmx_fio_int_fsync(cur);
841             if (rc != 0 && !ret)
842             {
843                 ret = cur;
844             }
845         }
846         cur = gmx_fio_get_next(cur);
847     }
848
849     /* in addition, we force these to be written out too, if they're being
850        redirected. We don't check for errors because errors most likely mean
851        that they're not redirected. */
852     fflush(stdout);
853     fflush(stderr);
854 #if (defined(HAVE_FSYNC))
855     /* again, fahcore defines HAVE_FSYNC and fsync() */
856     fsync(STDOUT_FILENO);
857     fsync(STDERR_FILENO);
858 #endif
859
860     return ret;
861 }
862
863
864 gmx_off_t gmx_fio_ftell(t_fileio* fio)
865 {
866     gmx_off_t ret = 0;
867
868     gmx_fio_lock(fio);
869     if (fio->fp)
870     {
871         ret = gmx_ftell(fio->fp);
872     }
873     gmx_fio_unlock(fio);
874     return ret;
875 }
876
877 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
878 {
879     int rc;
880
881     gmx_fio_lock(fio);
882     if (fio->fp)
883     {
884         rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
885     }
886     else
887     {
888         gmx_file(fio->fn);
889         rc = -1;
890     }
891     gmx_fio_unlock(fio);
892     return rc;
893 }
894
895 FILE *gmx_fio_getfp(t_fileio *fio)
896 {
897     FILE *ret = NULL;
898
899     gmx_fio_lock(fio);
900     if (fio->fp)
901     {
902         ret = fio->fp;
903     }
904     gmx_fio_unlock(fio);
905     return ret;
906 }
907
908 XDR *gmx_fio_getxdr(t_fileio* fio)
909 {
910     XDR *ret = NULL;
911
912     gmx_fio_lock(fio);
913     if (fio->xdr)
914     {
915         ret = fio->xdr;
916     }
917     gmx_fio_unlock(fio);
918
919     return ret;
920 }
921
922 gmx_bool gmx_fio_getread(t_fileio* fio)
923 {
924     gmx_bool ret;
925
926     gmx_fio_lock(fio);
927     ret = fio->bRead;
928     gmx_fio_unlock(fio);
929
930     return ret;
931 }
932
933 int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
934 {
935     int ret;
936
937     gmx_fio_lock(fio);
938     ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
939     gmx_fio_unlock(fio);
940
941     return ret;
942 }