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