mpd/src/input/cache/Manager.cxx

162 lines
3.6 KiB
C++
Raw Normal View History

/*
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.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 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "Manager.hxx"
#include "Config.hxx"
#include "Item.hxx"
#include "Lease.hxx"
#include "input/InputStream.hxx"
#include "fs/Traits.hxx"
#include "util/DeleteDisposer.hxx"
#include <string.h>
inline bool
InputCacheManager::ItemCompare::operator()(const InputCacheItem &a,
const char *b) const noexcept
{
return strcmp(a.GetUri(), b) < 0;
}
inline bool
InputCacheManager::ItemCompare::operator()(const char *a,
const InputCacheItem &b) const noexcept
{
return strcmp(a, b.GetUri()) < 0;
}
inline bool
InputCacheManager::ItemCompare::operator()(const InputCacheItem &a,
const InputCacheItem &b) const noexcept
{
return strcmp(a.GetUri(), b.GetUri()) < 0;
}
InputCacheManager::InputCacheManager(const InputCacheConfig &config) noexcept
:max_total_size(config.size)
{
}
InputCacheManager::~InputCacheManager() noexcept
{
items_by_time.clear_and_dispose(DeleteDisposer());
}
bool
InputCacheManager::IsEligible(const InputStream &input) noexcept
{
assert(input.IsReady());
return input.IsSeekable() && input.KnownSize() &&
input.GetSize() > 0 &&
input.GetSize() <= max_total_size / 2;
}
bool
InputCacheManager::Contains(const char *uri) noexcept
{
return Get(uri, false);
}
InputCacheLease
InputCacheManager::Get(const char *uri, bool create)
{
// TODO: allow caching remote files
if (!PathTraitsUTF8::IsAbsolute(uri))
return {};
auto iter = items_by_uri.find(uri, items_by_uri.key_comp());
if (iter != items_by_uri.end()) {
auto &item = *iter;
/* refresh */
items_by_time.erase(items_by_time.iterator_to(item));
items_by_time.push_back(item);
// TODO revalidate the cache item using the file's mtime?
// TODO if cache item contains error, retry now?
return InputCacheLease(item);
}
if (!create)
return {};
// TODO: wait for "ready" without blocking here
auto is = InputStream::OpenReady(uri, mutex);
if (!IsEligible(*is))
return {};
const size_t size = is->GetSize();
total_size += size;
while (total_size > max_total_size && EvictOldestUnused()) {}
auto *item = new InputCacheItem(std::move(is));
items_by_uri.insert(*item);
items_by_time.push_back(*item);
return InputCacheLease(*item);
}
void
InputCacheManager::Prefetch(const char *uri)
{
Get(uri, true);
}
void
InputCacheManager::Remove(InputCacheItem &item) noexcept
{
assert(total_size >= item.size());
total_size -= item.size();
items_by_time.erase(items_by_time.iterator_to(item));
items_by_uri.erase(items_by_uri.iterator_to(item));
}
void
InputCacheManager::Delete(InputCacheItem *item) noexcept
{
Remove(*item);
delete item;
}
InputCacheItem *
InputCacheManager::FindOldestUnused() noexcept
{
for (auto &i : items_by_time)
if (!i.IsInUse())
return &i;
return nullptr;
}
bool
InputCacheManager::EvictOldestUnused() noexcept
{
auto *item = FindOldestUnused();
if (item == nullptr)
return false;
Delete(item);
return true;
}