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