add 0.5 second sleep after closing JACK connection so that next startup/connect is...
[ardour.git] / libs / pbd / file_manager.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <cassert>
26 #include <cstdio>
27
28 #ifdef __APPLE__
29 #include <mach/mach_time.h>
30 #endif
31
32 #include "pbd/compose.h"
33 #include "pbd/file_manager.h"
34 #include "pbd/debug.h"
35
36 using namespace std;
37 using namespace PBD;
38
39 FileManager* FileDescriptor::_manager;
40
41 FileManager::FileManager ()
42         : _open (0)
43 {
44         struct rlimit rl;
45         int const r = getrlimit (RLIMIT_NOFILE, &rl);
46         
47         /* XXX: this is a bit arbitrary */
48         if (r == 0) {
49                 _max_open = rl.rlim_cur - 64;
50         } else {
51                 _max_open = 256;
52         }
53
54         DEBUG_TRACE (DEBUG::FileManager, string_compose ("FileManager can open up to %1 files.\n", _max_open));
55 }
56
57 void
58 FileManager::add (FileDescriptor* d)
59 {
60         Glib::Threads::Mutex::Lock lm (_mutex);
61         _files.push_back (d);
62 }
63
64 /** @return true on error, otherwise false */
65 bool
66 FileManager::allocate (FileDescriptor* d)
67 {
68         Glib::Threads::Mutex::Lock lm (_mutex);
69
70         if (!d->is_open()) {
71                 
72                 /* this file needs to be opened */
73                 
74                 if (_open == _max_open) {
75
76                         /* We already have the maximum allowed number of files opened, so we must try to close one.
77                            Find the unallocated, open file with the lowest last_used time.
78                         */
79
80                         double lowest_last_used = DBL_MAX;
81                         list<FileDescriptor*>::iterator oldest = _files.end ();
82
83                         for (list<FileDescriptor*>::iterator i = _files.begin(); i != _files.end(); ++i) {
84                                 if ((*i)->is_open() && (*i)->_refcount == 0) {
85                                         if ((*i)->_last_used < lowest_last_used) {
86                                                 lowest_last_used = (*i)->_last_used;
87                                                 oldest = i;
88                                         }
89                                 }
90                         }
91
92                         if (oldest == _files.end()) {
93                                 /* no unallocated and open files exist, so there's nothing we can do */
94                                 return true;
95                         }
96
97                         close (*oldest);
98                         DEBUG_TRACE (
99                                 DEBUG::FileManager,
100                                 string_compose (
101                                         "closed file for %1 to release file handle; now have %2 of %3 open\n",
102                                         (*oldest)->_path, _open, _max_open
103                                         )
104                                 );
105                 }
106
107                 if (d->open ()) {
108                         DEBUG_TRACE (DEBUG::FileManager, string_compose ("open of %1 failed.\n", d->_path));
109                         return true;
110                 }
111
112                 _open++;
113
114                 DEBUG_TRACE (DEBUG::FileManager, string_compose ("opened file for %1; now have %2 of %3 open.\n", d->_path, _open, _max_open));
115         }
116
117 #ifdef __APPLE__
118         d->_last_used = mach_absolute_time();
119 #else
120         struct timespec t;
121         clock_gettime (CLOCK_MONOTONIC, &t);
122         d->_last_used = t.tv_sec + (double) t.tv_nsec / 10e9;
123 #endif
124
125         d->_refcount++;
126         
127         return false;
128 }
129
130 /** Tell FileManager that a FileDescriptor is no longer needed for a given handle */
131 void
132 FileManager::release (FileDescriptor* d)
133 {
134         Glib::Threads::Mutex::Lock lm (_mutex);
135
136         d->_refcount--;
137         assert (d->_refcount >= 0);
138 }
139
140 /** Remove a file from our lists.  It will be closed if it is currently open. */
141 void
142 FileManager::remove (FileDescriptor* d)
143 {
144         Glib::Threads::Mutex::Lock lm (_mutex);
145
146         if (d->is_open ()) {
147                 close (d);
148                 DEBUG_TRACE (
149                         DEBUG::FileManager,
150                         string_compose ("closed file for %1; file is being removed; now have %2 of %3 open\n", d->_path, _open, _max_open)
151                         );
152         }
153
154         _files.remove (d);
155 }
156
157 void
158 FileManager::close (FileDescriptor* d)
159 {
160         /* we must have a lock on our mutex */
161
162         d->close ();
163         d->Closed (); /* EMIT SIGNAL */
164         _open--;
165 }
166
167 FileDescriptor::FileDescriptor (string const & n, bool w)
168         : _refcount (0)
169         , _last_used (0)
170         , _path (n)
171         , _writeable (w)
172 {
173
174 }
175
176 FileManager*
177 FileDescriptor::manager ()
178 {
179         if (_manager == 0) {
180                 _manager = new FileManager;
181         }
182
183         return _manager;
184 }
185
186 /** Release a previously allocated handle to this file */
187 void
188 FileDescriptor::release ()
189 {
190         manager()->release (this);
191 }
192
193
194
195 /** @param file_name Filename.
196  *  @param writeable true to open writeable, otherwise false.
197  *  @param mode Open mode for the file.
198  */
199
200 FdFileDescriptor::FdFileDescriptor (string const & file_name, bool writeable, mode_t mode)
201         : FileDescriptor (file_name, writeable)
202         , _fd (-1)
203         , _mode (mode)
204 {
205         manager()->add (this);
206 }
207
208 FdFileDescriptor::~FdFileDescriptor ()
209 {
210         manager()->remove (this);
211 }
212
213 bool
214 FdFileDescriptor::is_open () const
215 {
216         /* we must have a lock on the FileManager's mutex */
217
218         return _fd != -1;
219 }
220
221 bool
222 FdFileDescriptor::open ()
223 {
224         /* we must have a lock on the FileManager's mutex */
225         
226         _fd = ::open (_path.c_str(), _writeable ? (O_RDWR | O_CREAT) : O_RDONLY, _mode);
227         return (_fd == -1);
228 }
229
230 void
231 FdFileDescriptor::close ()
232 {
233         /* we must have a lock on the FileManager's mutex */
234
235         ::close (_fd);
236         _fd = -1;
237 }
238
239 /** @return fd, or -1 on error */
240 int
241 FdFileDescriptor::allocate ()
242 {
243         bool const f = manager()->allocate (this);
244         if (f) {
245                 return -1;
246         }
247
248         /* this is ok thread-wise because allocate () has incremented
249            the Descriptor's refcount, so the file will not be closed
250         */
251         return _fd;
252 }
253
254
255 void
256 FileDescriptor::set_path (const string& p)
257 {
258         _path = p;
259 }
260
261 /** @param file_name Filename.
262  *  @param mode Mode to pass to fopen.
263  */
264
265 StdioFileDescriptor::StdioFileDescriptor (string const & file_name, std::string const & mode)
266         : FileDescriptor (file_name, false)
267         , _file (0)
268         , _mode (mode)
269 {
270         manager()->add (this);
271 }
272
273 StdioFileDescriptor::~StdioFileDescriptor ()
274 {
275         manager()->remove (this);
276 }
277
278 bool
279 StdioFileDescriptor::is_open () const
280 {
281         /* we must have a lock on the FileManager's mutex */
282
283         return _file != 0;
284 }
285
286 bool
287 StdioFileDescriptor::open ()
288 {
289         /* we must have a lock on the FileManager's mutex */
290         
291         _file = fopen (_path.c_str(), _mode.c_str());
292         return (_file == 0);
293 }
294
295 void
296 StdioFileDescriptor::close ()
297 {
298         /* we must have a lock on the FileManager's mutex */
299
300         fclose (_file);
301         _file = 0;
302 }
303
304 /** @return FILE*, or 0 on error */
305 FILE*
306 StdioFileDescriptor::allocate ()
307 {
308         bool const f = manager()->allocate (this);
309         if (f) {
310                 return 0;
311         }
312
313         /* this is ok thread-wise because allocate () has incremented
314            the Descriptor's refcount, so the file will not be closed
315         */
316         return _file;
317 }