BUG: UListIO: byteSize overflowing on really big faceLists
[OpenFOAM-2.0.x.git] / src / OSspecific / POSIX / fileMonitor.C
blob31a734740d0fb814dfc0971dfd6bc99f865637e4
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of OpenFOAM.
11     OpenFOAM is free software: you can redistribute it and/or modify it
12     under the terms of the GNU General Public License as published by
13     the Free Software Foundation, either version 3 of the License, or
14     (at your option) any later version.
16     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19     for more details.
21     You should have received a copy of the GNU General Public License
22     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
24 \*----------------------------------------------------------------------------*/
26 #include "fileMonitor.H"
27 #include "IOstreams.H"
28 #include "Pstream.H"
29 #include "PackedList.H"
30 #include "PstreamReduceOps.H"
31 #include "OSspecific.H"
32 #include "regIOobject.H"     // for fileModificationSkew symbol
34 #ifdef FOAM_USE_INOTIFY
35 #   include <sys/inotify.h>
36 #   include <sys/ioctl.h>
37 #   include <errno.h>
38 #   define EVENT_SIZE  ( sizeof (struct inotify_event) )
39 #   define EVENT_LEN   (EVENT_SIZE + 16)
40 #   define EVENT_BUF_LEN     ( 1024 * EVENT_LEN )
41 #else
42 #   include "OSspecific.H"
43 #endif
45 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
47 defineTypeNameAndDebug(Foam::fileMonitor, 0);
49 const Foam::NamedEnum<Foam::fileMonitor::fileState, 3>
50     Foam::fileMonitor::fileStateNames_;
52 namespace Foam
54     template<>
55     const char* Foam::NamedEnum
56     <
57         Foam::fileMonitor::fileState,
58         3
59     >::names[] =
60     {
61         "unmodified",
62         "modified",
63         "deleted"
64     };
66     //- Reduction operator for PackedList of fileState
67     class reduceFileStates
68     {
69         public:
70         unsigned int operator()(const unsigned int x, const unsigned int y)
71         const
72         {
73             // x,y are sets of 2bits representing fileState
75             unsigned int mask = 3u;
76             unsigned int shift = 0;
77             unsigned int result = 0;
79             while (mask)
80             {
81                 // Combine state
82                 unsigned int xState = (x & mask) >> shift;
83                 unsigned int yState = (y & mask) >> shift;
85                 // Combine and add to result. Combine is such that UNMODIFIED
86                 // wins.
87                 unsigned int state = min(xState, yState);
88                 result |= (state << shift);
90                 shift += 2;
91                 mask <<= 2;
92             }
93             return result;
94         }
95     };
97     //- Combine operator for PackedList of fileState
98     class combineReduceFileStates
99     {
100         public:
101         void operator()(unsigned int& x, const unsigned int y) const
102         {
103             x = reduceFileStates()(x, y);
104         }
105     };
109     //-  Internal tracking via stat(3p) or inotify(7)
110     class fileMonitorWatcher
111     {
112     public:
114         const bool useInotify_;
116         // For inotify
118             //- File descriptor for the inotify instance
119             int inotifyFd_;
121             //- Current watchIDs and corresponding directory id
122             DynamicList<label> dirWatches_;
123             DynamicList<fileName> dirFiles_;
125         // For stat
127             //- From watch descriptor to modified time
128             DynamicList<time_t> lastMod_;
132         //- initialise inotify
133         inline fileMonitorWatcher(const bool useInotify, const label sz = 20)
134         :
135             useInotify_(useInotify)
136         {
137             if (useInotify_)
138             {
139                 #ifdef FOAM_USE_INOTIFY
140                 inotifyFd_ = inotify_init();
141                 dirWatches_.setCapacity(sz);
142                 dirFiles_.setCapacity(sz);
144                 if (inotifyFd_ < 0)
145                 {
146                     static bool hasWarned = false;
147                     if (!hasWarned)
148                     {
149                         hasWarned = true;
150                         WarningIn("fileMonitorWatcher(const bool, const label)")
151                             << "Failed allocating an inotify descriptor : "
152                             << string(strerror(errno)) << endl
153                             << "    Please increase the number of allowable "
154                             << "inotify instances" << endl
155                             << "    (/proc/sys/fs/inotify/max_user_instances"
156                             << " on Linux)" << endl
157                             << "    , switch off runTimeModifiable." << endl
158                             << "    or compile this file without "
159                             << "FOAM_USE_INOTIFY"
160                             << " to use time stamps instead of inotify." << endl
161                             << "    Continuing without additional file"
162                             << " monitoring."
163                             << endl;
164                     }
165                 }
166                 #else
167                     FatalErrorIn("fileMonitorWatcher(const bool, const label)")
168                         << "You selected inotify but this file was compiled"
169                         << " without FOAM_USE_INOTIFY"
170                         << "Please select another fileModification test method"
171                         << exit(FatalError);
172                 #endif
173             }
174             else
175             {
176                 lastMod_.setCapacity(sz);
177             }
178         }
180         //- remove all watches
181         inline ~fileMonitorWatcher()
182         {
183             #ifdef FOAM_USE_INOTIFY
184             if (useInotify_ && inotifyFd_ >= 0)
185             {
186                 forAll(dirWatches_, i)
187                 {
188                     if (dirWatches_[i] >= 0)
189                     {
190                         if (inotify_rm_watch(inotifyFd_, int(dirWatches_[i])))
191                         {
192                             WarningIn("fileMonitor::~fileMonitor()")
193                                 << "Failed deleting directory watch "
194                                 << dirWatches_[i] << endl;
195                         }
196                     }
197                 }
198             }
199             #endif
200         }
202         inline bool addWatch(const label watchFd, const fileName& fName)
203         {
204             if (useInotify_)
205             {
206                 if (inotifyFd_ < 0)
207                 {
208                     return false;
209                 }
211                 #ifdef FOAM_USE_INOTIFY
212                 // Add/retrieve watch on directory containing file.
213                 // Note that fName might be non-existing in special situations
214                 // (master-only reading for IODictionaries)
216                 const fileName dir = fName.path();
218                 label dirWatchID = -1;
219                 if (isDir(dir))
220                 {
221                     dirWatchID = inotify_add_watch
222                     (
223                         inotifyFd_,
224                         dir.c_str(),
225                         IN_CLOSE_WRITE
226                     );
228                     if (dirWatchID < 0)
229                     {
230                         FatalErrorIn("addWatch(const label, const fileName&)")
231                             << "Failed adding watch " << watchFd
232                             << " to directory " << fName << " due to "
233                             << string(strerror(errno))
234                             << exit(FatalError);
235                     }
236                 }
238                 if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
239                 {
240                     // Reuse of watchFd : should have dir watchID set to -1.
241                     FatalErrorIn("addWatch(const label, const fileName&)")
242                         << "Problem adding watch " << watchFd
243                         << " to file " << fName
244                         << abort(FatalError);
245                 }
247                 dirWatches_(watchFd) = dirWatchID;
248                 dirFiles_(watchFd) = fName.name();
249                 #endif
250             }
251             else
252             {
253                 if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
254                 {
255                     // Reuse of watchFd : should have lastMod set to 0.
256                     FatalErrorIn("addWatch(const label, const fileName&)")
257                         << "Problem adding watch " << watchFd
258                         << " to file " << fName
259                         << abort(FatalError);
260                 }
262                 lastMod_(watchFd) = lastModified(fName);
263             }
265             return true;
266         }
268         inline bool removeWatch(const label watchFd)
269         {
270             if (useInotify_)
271             {
272                 if (inotifyFd_ < 0)
273                 {
274                     return false;
275                 }
277                 dirWatches_[watchFd] = -1;
278             }
279             else
280             {
281                 lastMod_[watchFd] = 0;
282             }
283             return true;
284         }
286     };
290 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
292 void Foam::fileMonitor::checkFiles() const
294     if (useInotify_)
295     {
296         #ifdef FOAM_USE_INOTIFY
297         // Large buffer for lots of events
298         char buffer[EVENT_BUF_LEN];
300         while (true)
301         {
302             struct timeval zeroTimeout = {0, 0};
304             //- Pre-allocated structure containing file descriptors
305             fd_set fdSet;
306             // Add notify descriptor to select fd_set
307             FD_ZERO(&fdSet);
308             FD_SET(watcher_->inotifyFd_, &fdSet);
310             int ready = select
311             (
312                 watcher_->inotifyFd_+1,     // num filedescriptors in fdSet
313                 &fdSet,             // fdSet with only inotifyFd
314                 NULL,               // No writefds
315                 NULL,               // No errorfds
316                 &zeroTimeout        // eNo timeout
317             );
319             if (ready < 0)
320             {
321                 FatalErrorIn("fileMonitor::checkFiles()")
322                     << "Problem in issuing select."
323                     << abort(FatalError);
324             }
325             else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
326             {
327                 // Read events
328                 ssize_t nBytes = read
329                 (
330                     watcher_->inotifyFd_,
331                     buffer,
332                     EVENT_BUF_LEN
333                 );
335                 if (nBytes < 0)
336                 {
337                     FatalErrorIn("fileMonitor::checkFiles()")
338                         << "read of " << watcher_->inotifyFd_
339                         << " failed with " << label(nBytes)
340                         << abort(FatalError);
341                 }
343                 // Go through buffer, consuming events
344                 int i = 0;
345                 while (i < nBytes)
346                 {
347                     const struct inotify_event* inotifyEvent =
348                         reinterpret_cast<const struct inotify_event*>
349                         (
350                             &buffer[i]
351                         );
353                     //Pout<< "watchFd:" << inotifyEvent->wd << nl
354                     //    << "mask:" << inotifyEvent->mask << nl
355                     //  << endl;
356                     //Pout<< "file:" << fileName(inotifyEvent->name) << endl;
357                     //Pout<< "len:" << inotifyEvent->len << endl;
359                     if
360                     (
361                         (inotifyEvent->mask & IN_CLOSE_WRITE)
362                      && inotifyEvent->len
363                     )
364                     {
365                         // Search for file
366                         forAll(watcher_->dirWatches_, i)
367                         {
368                             label id = watcher_->dirWatches_[i];
369                             if
370                             (
371                                 id == inotifyEvent->wd
372                              && inotifyEvent->name == watcher_->dirFiles_[i]
373                             )
374                             {
375                                 // Correct directory and name
376                                 localState_[i] = MODIFIED;
377                             }
378                         }
379                     }
381                     i += EVENT_SIZE + inotifyEvent->len;
382                 }
383             }
384             else
385             {
386                 // No data
387                 return;
388             }
389         }
390         #endif
391     }
392     else
393     {
394         forAll(watcher_->lastMod_, watchFd)
395         {
396             time_t oldTime = watcher_->lastMod_[watchFd];
398             if (oldTime != 0)
399             {
400                 const fileName& fName = watchFile_[watchFd];
401                 time_t newTime = lastModified(fName);
403                 if (newTime == 0)
404                 {
405                     localState_[watchFd] = DELETED;
406                 }
407                 else
408                 {
409                     if (newTime > (oldTime + regIOobject::fileModificationSkew))
410                     {
411                         localState_[watchFd] = MODIFIED;
412                     }
413                     else
414                     {
415                         localState_[watchFd] = UNMODIFIED;
416                     }
417                 }
418             }
419         }
420     }
424 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
427 Foam::fileMonitor::fileMonitor(const bool useInotify)
429     useInotify_(useInotify),
430     localState_(20),
431     state_(20),
432     watchFile_(20),
433     freeWatchFds_(2),
434     watcher_(new fileMonitorWatcher(useInotify_, 20))
438 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
440 Foam::fileMonitor::~fileMonitor()
444 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
446 // Note: fName might not exist (on slaves if in master-only mode for
447 // regIOobject)
448 Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
450     label watchFd;
452     label sz = freeWatchFds_.size();
454     if (sz)
455     {
456         watchFd = freeWatchFds_[sz-1];
457         freeWatchFds_.setSize(sz-1);
458     }
459     else
460     {
461         watchFd = state_.size();
462     }
464     watcher_->addWatch(watchFd, fName);
466     if (debug)
467     {
468         Pout<< "fileMonitor : added watch " << watchFd << " on file "
469             << fName << endl;
470     }
472     if (watchFd < 0)
473     {
474         WarningIn("fileMonitor::addWatch(const fileName&)")
475             << "could not add watch for file " << fName << endl;
476     }
477     else
478     {
479         localState_(watchFd) = UNMODIFIED;
480         state_(watchFd) = UNMODIFIED;
481         watchFile_(watchFd) = fName;
482     }
483     return watchFd;
487 bool Foam::fileMonitor::removeWatch(const label watchFd)
489     if (debug)
490     {
491         Pout<< "fileMonitor : removing watch " << watchFd << " on file "
492             << watchFile_[watchFd] << endl;
493     }
495     freeWatchFds_.append(watchFd);
496     return watcher_->removeWatch(watchFd);
500 const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
502     return watchFile_[watchFd];
506 Foam::fileMonitor::fileState Foam::fileMonitor::getState(const label watchFd)
507 const
509     return state_[watchFd];
513 void Foam::fileMonitor::updateStates
515     const bool masterOnly,
516     const bool syncPar
517 ) const
519     if (Pstream::master() || !masterOnly)
520     {
521         // Update the localState_
522         checkFiles();
523     }
525     if (syncPar)
526     {
527         // Pack local state (might be on master only)
528         PackedList<2> stats(state_.size(), MODIFIED);
529         if (Pstream::master() || !masterOnly)
530         {
531             forAll(state_, watchFd)
532             {
533                 stats[watchFd] = static_cast<unsigned int>
534                 (
535                     localState_[watchFd]
536                 );
537             }
538         }
541         // Scatter or reduce to synchronise state
542         if (masterOnly)
543         {
544             // Scatter
545             if (stats.storage().size() == 1)
546             {
547                 Pstream::scatter(stats.storage()[0]);
548             }
549             else
550             {
551                 Pstream::listCombineScatter(stats.storage());
552             }
553         }
554         else
555         {
556             // Reduce
557             if (stats.storage().size() == 1)
558             {
559                 // Optimisation valid for most cases.
560                 reduce(stats.storage()[0], reduceFileStates());
561             }
562             else
563             {
564                 Pstream::listCombineGather
565                 (
566                     stats.storage(),
567                     combineReduceFileStates()
568                 );
569             }
570         }
573         // Update synchronised state
574         forAll(state_, watchFd)
575         {
576             // Assign synchronised state
577             unsigned int stat = stats[watchFd];
578             state_[watchFd] = fileState(stat);
580             if (!masterOnly)
581             {
582                 // Give warning for inconsistent state
583                 if (state_[watchFd] != localState_[watchFd])
584                 {
585                     if (debug)
586                     {
587                         Pout<< "fileMonitor : Delaying reading "
588                             << watchFile_[watchFd]
589                             << " due to inconsistent "
590                                "file time-stamps between processors"
591                             << endl;
592                     }
594                     WarningIn
595                     (
596                         "fileMonitor::updateStates"
597                         "(const bool, const bool) const"
598                     )   << "Delaying reading " << watchFile_[watchFd]
599                         << " due to inconsistent "
600                            "file time-stamps between processors" << endl;
601                 }
602             }
603         }
604     }
605     else
606     {
607         state_ = localState_;
608     }
612 void Foam::fileMonitor::setUnmodified(const label watchFd)
614     state_[watchFd] = UNMODIFIED;
615     localState_[watchFd] = UNMODIFIED;
617     if (!useInotify_)
618     {
619         watcher_->lastMod_[watchFd] = lastModified(watchFile_[watchFd]);
620     }
624 // ************************************************************************* //