summaryrefslogtreecommitdiff
path: root/libs/pbd
diff options
context:
space:
mode:
authorCarl Hetherington <carl@carlh.net>2010-05-16 20:54:50 +0000
committerCarl Hetherington <carl@carlh.net>2010-05-16 20:54:50 +0000
commitf1114dedeecf4279a4c19a85be0c11e9fde5610b (patch)
tree090a85771cda9f207a924e38c8f14290adbda033 /libs/pbd
parent50615cd17c06a5f4c8a196182407ceee7d182635 (diff)
Move FileManager code into libpbd. Use it for SMF read/write.
git-svn-id: svn://localhost/ardour2/branches/3.0@7108 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/pbd')
-rw-r--r--libs/pbd/debug.cc1
-rw-r--r--libs/pbd/file_manager.cc386
-rw-r--r--libs/pbd/pbd/debug.h1
-rw-r--r--libs/pbd/pbd/file_manager.h149
-rw-r--r--libs/pbd/wscript1
5 files changed, 538 insertions, 0 deletions
diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc
index 318f85a099..4f97ccff86 100644
--- a/libs/pbd/debug.cc
+++ b/libs/pbd/debug.cc
@@ -32,6 +32,7 @@ static std::map<const char*,uint64_t> _debug_bit_map;
uint64_t PBD::DEBUG::Stateful = PBD::new_debug_bit ("stateful");
uint64_t PBD::DEBUG::Properties = PBD::new_debug_bit ("properties");
+uint64_t PBD::DEBUG::FileManager = PBD::new_debug_bit ("filemanager");
uint64_t PBD::debug_bits = 0x0;
diff --git a/libs/pbd/file_manager.cc b/libs/pbd/file_manager.cc
new file mode 100644
index 0000000000..25d171b959
--- /dev/null
+++ b/libs/pbd/file_manager.cc
@@ -0,0 +1,386 @@
+/*
+ Copyright (C) 2010 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <cassert>
+#include <iostream>
+#include "pbd/compose.h"
+#include "pbd/file_manager.h"
+#include "pbd/debug.h"
+
+using namespace std;
+using namespace PBD;
+
+FileManager* FileDescriptor::_manager;
+
+namespace PBD {
+
+/** Class to limit the number of files held open */
+class FileManager
+{
+public:
+ FileManager ();
+
+ void add (FileDescriptor *);
+ void remove (FileDescriptor *);
+
+ void release (FileDescriptor *);
+ bool allocate (FileDescriptor *);
+
+private:
+
+ void close (FileDescriptor *);
+
+ std::list<FileDescriptor*> _files; ///< files we know about
+ Glib::Mutex _mutex; ///< mutex for _files, _open and FileDescriptor contents
+ int _open; ///< number of open files
+ int _max_open; ///< maximum number of open files
+};
+
+}
+
+FileManager::FileManager ()
+ : _open (0)
+{
+ struct rlimit rl;
+ int const r = getrlimit (RLIMIT_NOFILE, &rl);
+
+ /* XXX: this is a bit arbitrary */
+ if (r == 0) {
+ _max_open = rl.rlim_cur - 64;
+ } else {
+ _max_open = 256;
+ }
+
+ DEBUG_TRACE (DEBUG::FileManager, string_compose ("FileManager can open up to %1 files.\n", _max_open));
+}
+
+void
+FileManager::add (FileDescriptor* d)
+{
+ Glib::Mutex::Lock lm (_mutex);
+ _files.push_back (d);
+}
+
+/** @return true on error, otherwise false */
+bool
+FileManager::allocate (FileDescriptor* d)
+{
+ Glib::Mutex::Lock lm (_mutex);
+
+ if (!d->is_open()) {
+
+ /* this file needs to be opened */
+
+ if (_open == _max_open) {
+
+ /* We already have the maximum allowed number of files opened, so we must try to close one.
+ Find the unallocated, open file with the lowest last_used time.
+ */
+
+ double lowest_last_used = DBL_MAX;
+ list<FileDescriptor*>::iterator oldest = _files.end ();
+
+ for (list<FileDescriptor*>::iterator i = _files.begin(); i != _files.end(); ++i) {
+ if ((*i)->is_open() && (*i)->_refcount == 0) {
+ if ((*i)->_last_used < lowest_last_used) {
+ lowest_last_used = (*i)->_last_used;
+ oldest = i;
+ }
+ }
+ }
+
+ if (oldest == _files.end()) {
+ /* no unallocated and open files exist, so there's nothing we can do */
+ return true;
+ }
+
+ close (*oldest);
+ DEBUG_TRACE (
+ DEBUG::FileManager,
+ string_compose (
+ "closed file for %1 to release file handle; now have %2 of %3 open\n",
+ (*oldest)->_name, _open, _max_open
+ )
+ );
+ }
+
+ if (d->open ()) {
+ DEBUG_TRACE (DEBUG::FileManager, string_compose ("open of %1 failed.\n", d->_name));
+ return true;
+ }
+
+ _open++;
+
+ DEBUG_TRACE (DEBUG::FileManager, string_compose ("opened file for %1; now have %2 of %3 open.\n", d->_name, _open, _max_open));
+ }
+
+ struct timespec t;
+ clock_gettime (CLOCK_MONOTONIC, &t);
+ d->_last_used = t.tv_sec + (double) t.tv_nsec / 10e9;
+
+ d->_refcount++;
+
+ return false;
+}
+
+/** Tell FileManager that a FileDescriptor is no longer needed for a given handle */
+void
+FileManager::release (FileDescriptor* d)
+{
+ Glib::Mutex::Lock lm (_mutex);
+
+ d->_refcount--;
+ assert (d->_refcount >= 0);
+}
+
+/** Remove a file from our lists. It will be closed if it is currently open. */
+void
+FileManager::remove (FileDescriptor* d)
+{
+ Glib::Mutex::Lock lm (_mutex);
+
+ if (d->is_open ()) {
+ close (d);
+ DEBUG_TRACE (
+ DEBUG::FileManager,
+ string_compose ("closed file for %1; file is being removed; now have %2 of %3 open\n", d->_name, _open, _max_open)
+ );
+ }
+
+ _files.remove (d);
+}
+
+void
+FileManager::close (FileDescriptor* d)
+{
+ /* we must have a lock on our mutex */
+
+ d->close ();
+ d->Closed (); /* EMIT SIGNAL */
+ _open--;
+}
+
+FileDescriptor::FileDescriptor (string const & n, bool w)
+ : _refcount (0)
+ , _last_used (0)
+ , _name (n)
+ , _writeable (w)
+{
+
+}
+
+FileManager*
+FileDescriptor::manager ()
+{
+ if (_manager == 0) {
+ _manager = new FileManager;
+ }
+
+ return _manager;
+}
+
+/** Release a previously allocated handle to this file */
+void
+FileDescriptor::release ()
+{
+ manager()->release (this);
+}
+
+/** @param n Filename.
+ * @param w true to open writeable, otherwise false.
+ * @param i SF_INFO for the file.
+ */
+
+SndFileDescriptor::SndFileDescriptor (string const & n, bool w, SF_INFO* i)
+ : FileDescriptor (n, w)
+ , _sndfile (0)
+ , _info (i)
+{
+ manager()->add (this);
+}
+
+SndFileDescriptor::~SndFileDescriptor ()
+{
+ manager()->remove (this);
+}
+
+/** @return SNDFILE*, or 0 on error */
+SNDFILE*
+SndFileDescriptor::allocate ()
+{
+ bool const f = manager()->allocate (this);
+ if (f) {
+ return 0;
+ }
+
+ /* this is ok thread-wise because allocate () has incremented
+ the Descriptor's refcount, so the file will not be closed
+ */
+ return _sndfile;
+}
+
+void
+SndFileDescriptor::close ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ sf_close (_sndfile);
+ _sndfile = 0;
+}
+
+bool
+SndFileDescriptor::is_open () const
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ return _sndfile != 0;
+}
+
+bool
+SndFileDescriptor::open ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ _sndfile = sf_open (_name.c_str(), _writeable ? SFM_RDWR : SFM_READ, _info);
+ return (_sndfile == 0);
+}
+
+
+/** @param n Filename.
+ * @param w true to open writeable, otherwise false.
+ * @param m Open mode for the file.
+ */
+
+FdFileDescriptor::FdFileDescriptor (string const & n, bool w, mode_t m)
+ : FileDescriptor (n, w)
+ , _fd (-1)
+ , _mode (m)
+{
+ manager()->add (this);
+}
+
+FdFileDescriptor::~FdFileDescriptor ()
+{
+ manager()->remove (this);
+}
+
+bool
+FdFileDescriptor::is_open () const
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ return _fd != -1;
+}
+
+bool
+FdFileDescriptor::open ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ _fd = ::open (_name.c_str(), _writeable ? (O_RDWR | O_CREAT) : O_RDONLY, _mode);
+ return (_fd == -1);
+}
+
+void
+FdFileDescriptor::close ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ ::close (_fd);
+ _fd = -1;
+}
+
+/** @return fd, or -1 on error */
+int
+FdFileDescriptor::allocate ()
+{
+ bool const f = manager()->allocate (this);
+ if (f) {
+ return -1;
+ }
+
+ /* this is ok thread-wise because allocate () has incremented
+ the Descriptor's refcount, so the file will not be closed
+ */
+ return _fd;
+}
+
+
+/** @param n Filename.
+ * @param w true to open writeable, otherwise false.
+ */
+
+StdioFileDescriptor::StdioFileDescriptor (string const & n, std::string const & m)
+ : FileDescriptor (n, false)
+ , _file (0)
+ , _mode (m)
+{
+ manager()->add (this);
+}
+
+StdioFileDescriptor::~StdioFileDescriptor ()
+{
+ manager()->remove (this);
+}
+
+bool
+StdioFileDescriptor::is_open () const
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ return _file != 0;
+}
+
+bool
+StdioFileDescriptor::open ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ _file = fopen (_name.c_str(), _mode.c_str());
+ return (_file == 0);
+}
+
+void
+StdioFileDescriptor::close ()
+{
+ /* we must have a lock on the FileManager's mutex */
+
+ fclose (_file);
+ _file = 0;
+}
+
+/** @return FILE*, or 0 on error */
+FILE*
+StdioFileDescriptor::allocate ()
+{
+ bool const f = manager()->allocate (this);
+ if (f) {
+ return 0;
+ }
+
+ /* this is ok thread-wise because allocate () has incremented
+ the Descriptor's refcount, so the file will not be closed
+ */
+ return _file;
+}
diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h
index a45a867bd2..d3ce495a1b 100644
--- a/libs/pbd/pbd/debug.h
+++ b/libs/pbd/pbd/debug.h
@@ -39,6 +39,7 @@ namespace PBD {
extern uint64_t Stateful;
extern uint64_t Properties;
+ extern uint64_t FileManager;
}
}
diff --git a/libs/pbd/pbd/file_manager.h b/libs/pbd/pbd/file_manager.h
new file mode 100644
index 0000000000..3f708eb44e
--- /dev/null
+++ b/libs/pbd/pbd/file_manager.h
@@ -0,0 +1,149 @@
+/*
+ Copyright (C) 2010 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __pbd_file_manager_h__
+#define __pbd_file_manager_h__
+
+#include <sys/types.h>
+#include <string>
+#include <map>
+#include <sndfile.h>
+#include <glibmm/thread.h>
+#include "pbd/signals.h"
+
+namespace PBD {
+
+class FileManager;
+
+/** Parent class for FileDescriptors.
+ *
+ * When a subclass is instantiated, the file it describes is added to a
+ * list. The FileDescriptor can be `allocated', meaning that its
+ * file will be opened on the filesystem, and can then be `released'.
+ * FileDescriptors are reference counted as they are allocated and
+ * released. When a descriptor's refcount is 0, the file on the
+ * filesystem is eligible to be closed if necessary to free up file
+ * handles for other files.
+ *
+ * The upshot of all this is that Ardour can manage the number of
+ * open files to stay within limits imposed by the operating system.
+ */
+
+class FileDescriptor
+{
+public:
+ FileDescriptor (std::string const &, bool);
+ virtual ~FileDescriptor () {}
+
+ void release ();
+
+ /** Emitted when the file is closed */
+ PBD::Signal0<void> Closed;
+
+protected:
+
+ friend class FileManager;
+
+ /* These methods and variables must be called / accessed
+ with a lock held on the FileManager's mutex
+ */
+
+ /** @return false on success, true on failure */
+ virtual bool open () = 0;
+ virtual void close () = 0;
+ virtual bool is_open () const = 0;
+
+ int _refcount; ///< number of active users of this file
+ double _last_used; ///< monotonic time that this file was last allocated
+ std::string _name; ///< filename
+ bool _writeable; ///< true if it should be opened writeable, otherwise false
+
+ FileManager* manager ();
+
+private:
+
+ static FileManager* _manager;
+};
+
+/** FileDescriptor for a file to be opened using libsndfile */
+class SndFileDescriptor : public FileDescriptor
+{
+public:
+ SndFileDescriptor (std::string const &, bool, SF_INFO *);
+ ~SndFileDescriptor ();
+
+ SNDFILE* allocate ();
+
+private:
+
+ friend class FileManager;
+
+ bool open ();
+ void close ();
+ bool is_open () const;
+
+ SNDFILE* _sndfile; ///< SNDFILE* pointer, or 0 if the file is closed
+ SF_INFO* _info; ///< libsndfile's info for this file
+};
+
+/** FileDescriptor for a file to be opened using POSIX open */
+class FdFileDescriptor : public FileDescriptor
+{
+public:
+ FdFileDescriptor (std::string const &, bool, mode_t);
+ ~FdFileDescriptor ();
+
+ int allocate ();
+
+private:
+
+ friend class FileManager;
+
+ bool open ();
+ void close ();
+ bool is_open () const;
+
+ int _fd; ///< file descriptor, or -1 if the file is closed
+ mode_t _mode; ///< mode to use when creating files
+};
+
+/** FileDescriptor for a file opened using stdio */
+class StdioFileDescriptor : public FileDescriptor
+{
+public:
+ StdioFileDescriptor (std::string const &, std::string const &);
+ ~StdioFileDescriptor ();
+
+ FILE* allocate ();
+
+private:
+
+ friend class FileManager;
+
+ bool open ();
+ void close ();
+ bool is_open () const;
+
+ FILE* _file;
+ std::string _mode;
+};
+
+}
+
+#endif
diff --git a/libs/pbd/wscript b/libs/pbd/wscript
index 81174d1262..73d6c3023c 100644
--- a/libs/pbd/wscript
+++ b/libs/pbd/wscript
@@ -67,6 +67,7 @@ def build(bld):
error.cc
filesystem.cc
filesystem_paths.cc
+ file_manager.cc
file_utils.cc
fpu.cc
id.cc