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