Permalink
Browse files

o Build infrastructure to re-use file handler objects. Files seem to

  be re-opened several times sometimes by clients. This makes it cheap.
  (TODO: proper closing of files etc).
  • Loading branch information...
1 parent 0c8561b commit 15b305a6feaa9c974e1877192dec90c3018a7360 @hzeller committed Sep 14, 2012
Showing with 203 additions and 55 deletions.
  1. +1 −1 Makefile
  2. +32 −45 convolver.cc
  3. +75 −0 file-handler-cache.cc
  4. +61 −0 file-handler-cache.h
  5. +25 −0 file-handler.h
  6. +5 −1 filter-interface.h
  7. +4 −8 fuse-convolve.c
View
2 Makefile
@@ -4,7 +4,7 @@ CFLAGS=-D_FILE_OFFSET_BITS=64 -Wall -g -O2
CXXFLAGS=$(CFLAGS)
LDFLAGS=-lfuse -lsndfile -lzita-convolver
-fuse-convolve: fuse-convolve.o convolver.o conversion-buffer.o zita-audiofile.o zita-config.o zita-fconfig.o zita-sstring.o
+fuse-convolve: fuse-convolve.o convolver.o conversion-buffer.o zita-audiofile.o zita-config.o zita-fconfig.o zita-sstring.o file-handler-cache.o
$(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS)
clean:
View
77 convolver.cc
@@ -15,6 +15,7 @@
#include <FLAC/metadata.h>
#include <errno.h>
+#include <fcntl.h>
#include <sndfile.h>
#include <stdio.h>
#include <string.h>
@@ -23,30 +24,22 @@
#include <sys/types.h>
#include <unistd.h>
-#include <boost/thread/locks.hpp>
-#include <boost/thread/mutex.hpp>
#include <string>
#include <map>
+#include "file-handler.h"
+#include "file-handler-cache.h"
#include "filter-interface.h"
#include "conversion-buffer.h"
#include "zita-config.h"
const char *global_zita_config_dir = NULL;
namespace {
-class FileFilter : public filter_object_t {
-public:
- // Returns bytes read or a negative value indicating a negative errno.
- virtual int Read(char *buf, size_t size, off_t offset) = 0;
- virtual int Stat(struct stat *st) = 0;
- virtual int Close() = 0;
- virtual ~FileFilter() {}
-};
// Very simple filter that just passes the original file through. Used for
// everything that is not a sound-file.
-class PassThroughFilter : public FileFilter {
+class PassThroughFilter : public FileHandler {
public:
PassThroughFilter(int filedes, const char *path) : filedes_(filedes) {
fprintf(stderr, "Creating PassThrough filter for '%s'\n", path);
@@ -66,14 +59,14 @@ class PassThroughFilter : public FileFilter {
const int filedes_;
};
-class SndFileFilter :
- public FileFilter,
+class SndFileHandler :
+ public FileHandler,
public ConversionBuffer::SoundSource {
public:
- // Attempt to create a SndFileFilter from the given file descriptor. This
+ // Attempt to create a SndFileHandler from the given file descriptor. This
// returns NULL if this is not a sound-file or if there is no available
// convolution filter configuration available.
- static FileFilter *Create(int filedes, const char *path) {
+ static FileHandler *Create(int filedes, const char *path) {
struct SF_INFO in_info;
memset(&in_info, 0, sizeof(in_info));
SNDFILE *snd = sf_open_fd(filedes, SFM_READ, &in_info, 0);
@@ -103,10 +96,10 @@ class SndFileFilter :
} else {
fprintf(stderr, "- found.\n");
}
- return new SndFileFilter(path, filedes, snd, in_info, config_path);
+ return new SndFileHandler(path, filedes, snd, in_info, config_path);
}
- virtual ~SndFileFilter() {
+ virtual ~SndFileHandler() {
if (zita_.convproc) {
zita_.convproc->stop_process();
zita_.convproc->cleanup();
@@ -160,7 +153,7 @@ class SndFileFilter :
}
private:
- SndFileFilter(const char *path, int filedes, SNDFILE *snd_in,
+ SndFileHandler(const char *path, int filedes, SNDFILE *snd_in,
const SF_INFO &in_info,
const char* config_path)
: filedes_(filedes), snd_in_(snd_in), total_frames_(in_info.frames),
@@ -400,13 +393,11 @@ class SndFileFilter :
} // namespace
-typedef std::map<std::string, FileFilter*> FileFilterMap;
-static FileFilterMap open_files_;
-static boost::mutex open_files_mutex_;
+static FileHandlerCache open_files_(10);
-static FileFilter *CreateFilterFromFileType(int filedes,
+static FileHandler *CreateFilterFromFileType(int filedes,
const char *underlying_file) {
- FileFilter *filter = SndFileFilter::Create(filedes, underlying_file);
+ FileHandler *filter = SndFileHandler::Create(filedes, underlying_file);
if (filter != NULL) return filter;
fprintf(stderr, "Cound't create filtered output\n");
@@ -415,42 +406,38 @@ static FileFilter *CreateFilterFromFileType(int filedes,
}
// Implementation of the C functions in filter-interface.h
-struct filter_object_t *create_filter(int filedes, const char *fs_path,
+struct filter_object_t *create_filter(const char *fs_path,
const char *underlying_path) {
- FileFilter *filter = CreateFilterFromFileType(filedes, underlying_path);
- open_files_mutex_.lock();
- open_files_[fs_path] = filter;
- open_files_mutex_.unlock();
- return filter;
+ FileHandler *handler = open_files_.FindAndPin(fs_path);
+ if (handler == NULL) {
+ int filedes = open(underlying_path, O_RDONLY);
+ handler = CreateFilterFromFileType(filedes, underlying_path);
+ handler = open_files_.InsertPinned(fs_path, handler);
+ }
+ return handler;
}
int read_from_filter(struct filter_object_t *filter,
char *buf, size_t size, off_t offset) {
- return reinterpret_cast<FileFilter*>(filter)->Read(buf, size, offset);
+ return reinterpret_cast<FileHandler*>(filter)->Read(buf, size, offset);
}
int fill_stat_by_filename(const char *fs_path, struct stat *st) {
- boost::lock_guard<boost::mutex> l(open_files_mutex_);
- FileFilterMap::const_iterator found = open_files_.find(fs_path);
- if (found == open_files_.end())
+ FileHandler *handler = open_files_.FindAndPin(fs_path);
+ if (handler == 0)
return -1;
- return found->second->Stat(st);
+ ssize_t result = handler->Stat(st);
+ open_files_.Unpin(fs_path);
+ return result;
}
int fill_fstat_file(struct filter_object_t *filter, struct stat *st) {
- return reinterpret_cast<FileFilter*>(filter)->Stat(st);
+ return reinterpret_cast<FileHandler*>(filter)->Stat(st);
}
int close_filter(const char *fs_path, struct filter_object_t *filter) {
- FileFilter *file_filter = reinterpret_cast<FileFilter*>(filter);
- int result = file_filter->Close();
- open_files_mutex_.lock();
- FileFilterMap::iterator found = open_files_.find(fs_path);
- if (found != open_files_.end() && found->second == file_filter) {
- open_files_.erase(found);
- }
- open_files_mutex_.unlock();
- delete file_filter;
- return result;
+ open_files_.Unpin(fs_path);
+ // TODO close file.
+ return 0;
}
void initialize_convolver_filter(const char *zita_config_dir) {
View
75 file-handler-cache.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <map>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include "file-handler.h"
+#include "file-handler-cache.h"
+
+struct FileHandlerCache::Entry {
+ Entry(FileHandler *h) : handler(h), references(0) {}
+ ~Entry() { delete handler; }
+
+ FileHandler *const handler;
+ int references;
+};
+
+FileHandler *FileHandlerCache::InsertPinned(const std::string &key,
+ FileHandler *handler) {
+ boost::lock_guard<boost::mutex> l(mutex_);
+ CacheMap::iterator ins
+ = cache_.insert(std::make_pair(key, (Entry*)NULL)).first;
+ if (ins->second == NULL) { // new entry
+ ins->second = new Entry(handler);
+ } else {
+ delete handler;
+ }
+ ++ins->second->references;
+ if (cache_.size() > max_size_) {
+ CleanupUnreferenced();
+ }
+ return ins->second->handler;
+}
+
+FileHandler *FileHandlerCache::FindAndPin(const std::string &key) {
+ boost::lock_guard<boost::mutex> l(mutex_);
+ CacheMap::iterator found = cache_.find(key);
+ if (found == cache_.end())
+ return NULL;
+ ++found->second->references;
+ return found->second->handler;
+}
+
+void FileHandlerCache::Unpin(const std::string &key) {
+ boost::lock_guard<boost::mutex> l(mutex_);
+ CacheMap::iterator found = cache_.find(key);
+ assert(found != cache_.end());
+ --found->second->references;
+}
+
+void FileHandlerCache::CleanupUnreferenced() {
+ for (CacheMap::iterator it = cache_.begin(); it != cache_.end(); ++it) {
+ if (it->second->references == 0) {
+ fprintf(stderr, "cleanup %s\n", it->first.c_str());
+ delete it->second;
+ cache_.erase(it);
+ }
+ }
+}
View
61 file-handler-cache.h
@@ -0,0 +1,61 @@
+// -*- c++ -*-
+// Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#ifndef _FUSE_CONVOLVER_FILE_HANDLER_CACHE_
+#define _FUSE_CONVOLVER_FILE_HANDLER_CACHE_
+
+#include <map>
+#include <string>
+#include <boost/thread/mutex.hpp>
+
+class FileHandler;
+
+// Cache of in-use file handlers. We sometimes get multiple requests for the
+// same file, so we want to map them all to the same FileHandler.
+//
+// This Cache manages the lifecycle of a FileHandler object; the user creates
+// it, but this Cache handles deletion.
+// This container is thread-safe.
+class FileHandlerCache {
+public:
+ FileHandlerCache(int max_size) : max_size_(max_size) {}
+
+ // Insert a new object under the given key.
+ // Ownership is handed over to this map.
+ // If there was already an object stored under that key, that is returned
+ // instead and the new object discarded.
+ FileHandler *InsertPinned(const std::string &key, FileHandler *handler);
+
+ // Find an object in this map and pin it down. You've to Unpin() it after
+ // use.
+ FileHandler *FindAndPin(const std::string &key);
+
+ // Unpin object. If the last object is unpinned, the PinnedMap may decide
+ // to delete it later (though typically will keep it around for a while).
+ void Unpin(const std::string &key);
+
+ private:
+ struct Entry;
+ typedef std::map<std::string, Entry*> CacheMap;
+
+ void CleanupUnreferenced();
+
+ const size_t max_size_;
+ boost::mutex mutex_;
+ CacheMap cache_;
+};
+
+#endif // _FUSE_CONVOLVER_FILE_HANDLER_CACHE_
View
25 file-handler.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#include "filter-interface.h"
+
+class FileHandler : public filter_object_t {
+public:
+ // Returns bytes read or a negative value indicating a negative errno.
+ virtual int Read(char *buf, size_t size, off_t offset) = 0;
+ virtual int Stat(struct stat *st) = 0;
+ virtual int Close() = 0;
+ virtual ~FileHandler() {}
+};
View
6 filter-interface.h
@@ -1,3 +1,4 @@
+// -*- c++ -*-
// Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
//
// This program is free software; you can redistribute it and/or modify
@@ -16,6 +17,8 @@
// Simple interface to hook the (pure C) fuse-convolve code into some
// implementation of the filter (which we choose to do with C++).
+#ifndef _FUSE_CONVOLVER_FILTER_INTERFACE_H
+#define _FUSE_CONVOLVER_FILTER_INTERFACE_H
#include <unistd.h>
#ifdef __cplusplus
@@ -29,7 +32,7 @@ void initialize_convolver_filter(const char *zita_config);
// Create a new filter given the open filedescriptor and the path. Returns
// that filter in an opaque filter_object_t*
-struct filter_object_t *create_filter(int filedes, const char *fs_path,
+struct filter_object_t *create_filter(const char *fs_path,
const char *underlying_path);
// Read from the given filter at the file-offset "offset, up to "size" bytes
@@ -49,3 +52,4 @@ int close_filter(const char *path, struct filter_object_t *filter);
} // extern "C"
#endif /* __cplusplus */
+#endif // _FUSE_CONVOLVER_FILTER_INTERFACE_H
View
12 fuse-convolve.c
@@ -131,12 +131,6 @@ static int fuseconv_readlink(const char *path, char *buf, size_t size) {
static int fuseconv_open(const char *path, struct fuse_file_info *fi) {
fprintf(stderr, "[===== open('%s')\n", path);
- char path_buf[PATH_MAX];
- const char *orig_path = assemble_orig_path(path_buf, path);
- const int fd = open(orig_path, fi->flags);
- if (fd == -1)
- return -errno;
-
// We want to be allowed to only return part of the requested data in read().
// That way, we can separate reading the ID3-tags from
// decoding of the music stream - that way indexing should be fast.
@@ -146,8 +140,10 @@ static int fuseconv_open(const char *path, struct fuse_file_info *fi) {
// The file-handle has the neat property to be 64 bit - so we can actually
// store a pointer to our filter object in there :)
// (Yay, someone was thinking while developing that API).
- fi->fh = (uint64_t) create_filter(fd, path, orig_path);
- return 0;
+ char path_buf[PATH_MAX];
+ const char *orig_path = assemble_orig_path(path_buf, path);
+ fi->fh = (uint64_t) create_filter(path, orig_path);
+ return fi->fh == 0 ? -1 : 0;
}
static int fuseconv_read(const char *path, char *buf, size_t size, off_t offset,

0 comments on commit 15b305a

Please sign in to comment.