Compare commits
1 Commits
934d400cb4
...
v2.1.0-rc-
Author | SHA1 | Date | |
---|---|---|---|
8dd46b8ad8 |
@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
* Ability to choose between RocksDB and SQLite databases
|
* Ability to choose between RocksDB and SQLite databases
|
||||||
* Added direct reads and implemented download fallback
|
* Added direct reads and implemented download fallback
|
||||||
* Comprehensive WinFSP and FUSE unit tests, including remote testing
|
|
||||||
* Corrected file times on S3 and Sia providers
|
* Corrected file times on S3 and Sia providers
|
||||||
* Corrected handling of `chown()` and `chmod()`
|
* Corrected handling of `chown()` and `chmod()`
|
||||||
* Fixed erroneous download of chunks after resize
|
* Fixed erroneous download of chunks after resize
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE2_HPP_
|
#ifndef REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE_HPP_
|
||||||
#define REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE2_HPP_
|
#define REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE_HPP_
|
||||||
|
|
||||||
#include "file_manager/open_file_base.hpp"
|
#include "file_manager/ring_buffer_base.hpp"
|
||||||
|
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ namespace repertory {
|
|||||||
class i_provider;
|
class i_provider;
|
||||||
class i_upload_manager;
|
class i_upload_manager;
|
||||||
|
|
||||||
class direct_open_file final : public open_file_base {
|
class direct_open_file final : public ring_buffer_base {
|
||||||
public:
|
public:
|
||||||
direct_open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
direct_open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
||||||
filesystem_item fsi, i_provider &provider);
|
filesystem_item fsi, i_provider &provider);
|
||||||
@ -42,51 +42,31 @@ public:
|
|||||||
direct_open_file(const direct_open_file &) noexcept = delete;
|
direct_open_file(const direct_open_file &) noexcept = delete;
|
||||||
direct_open_file(direct_open_file &&) noexcept = delete;
|
direct_open_file(direct_open_file &&) noexcept = delete;
|
||||||
auto operator=(direct_open_file &&) noexcept -> direct_open_file & = delete;
|
auto operator=(direct_open_file &&) noexcept -> direct_open_file & = delete;
|
||||||
auto operator=(const direct_open_file &) noexcept
|
auto
|
||||||
-> direct_open_file & = delete;
|
operator=(const direct_open_file &) noexcept -> direct_open_file & = delete;
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr const auto ring_size{5U};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::dynamic_bitset<> ring_state_;
|
std::array<data_buffer, min_ring_size> ring_data_;
|
||||||
std::size_t total_chunks_;
|
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::condition_variable chunk_notify_;
|
[[nodiscard]] auto on_check_start() -> bool override;
|
||||||
mutable std::mutex chunk_mtx_;
|
|
||||||
std::mutex read_mtx_;
|
|
||||||
std::unique_ptr<std::thread> reader_thread_;
|
|
||||||
std::size_t ring_begin_{};
|
|
||||||
std::array<data_buffer, ring_size> ring_data_;
|
|
||||||
std::size_t ring_end_{};
|
|
||||||
std::size_t ring_pos_{};
|
|
||||||
stop_type stop_requested_{false};
|
|
||||||
|
|
||||||
private:
|
[[nodiscard]] auto
|
||||||
void background_reader_thread();
|
on_chunk_downloaded(std::size_t /* chunk */,
|
||||||
|
const data_buffer & /* buffer */) -> api_error override {
|
||||||
auto download_chunk(std::size_t chunk, bool skip_active) -> api_error;
|
return api_error::success;
|
||||||
|
|
||||||
public:
|
|
||||||
auto close() -> bool override;
|
|
||||||
|
|
||||||
void forward(std::size_t count);
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_read_state() const -> boost::dynamic_bitset<> override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_read_state(std::size_t chunk) const -> bool override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_total_chunks() const -> std::size_t {
|
|
||||||
return total_chunks_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto is_complete() const -> bool override { return false; }
|
[[nodiscard]] auto
|
||||||
|
on_read_chunk(std::size_t chunk, std::size_t read_size,
|
||||||
|
std::uint64_t read_offset, data_buffer &data,
|
||||||
|
std::size_t &bytes_read) -> api_error override;
|
||||||
|
|
||||||
[[nodiscard]] auto is_write_supported() const -> bool override {
|
[[nodiscard]] auto use_buffer(std::size_t chunk,
|
||||||
return false;
|
std::function<api_error(data_buffer &)> func)
|
||||||
}
|
-> api_error override;
|
||||||
|
|
||||||
|
public:
|
||||||
[[nodiscard]] auto native_operation(native_operation_callback /* callback */)
|
[[nodiscard]] auto native_operation(native_operation_callback /* callback */)
|
||||||
-> api_error override {
|
-> api_error override {
|
||||||
return api_error::not_supported;
|
return api_error::not_supported;
|
||||||
@ -97,25 +77,7 @@ public:
|
|||||||
-> api_error override {
|
-> api_error override {
|
||||||
return api_error::not_supported;
|
return api_error::not_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto read(std::size_t read_size, std::uint64_t read_offset,
|
|
||||||
data_buffer &data) -> api_error override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto resize(std::uint64_t /* size */) -> api_error override {
|
|
||||||
return api_error::not_supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reverse(std::size_t count);
|
|
||||||
|
|
||||||
void set_api_path(const std::string &api_path) override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto write(std::uint64_t /* write_offset */,
|
|
||||||
const data_buffer & /* data */,
|
|
||||||
std::size_t & /* bytes_written */)
|
|
||||||
-> api_error override {
|
|
||||||
return api_error::not_supported;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif // REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE2_HPP_
|
#endif // REPERTORY_INCLUDE_FILE_MANAGER_DIRECT_OPEN_FILE_HPP_
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "file_manager/open_file_base.hpp"
|
#include "file_manager/open_file_base.hpp"
|
||||||
|
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
|
#include "utils/types/file/i_file.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
class i_provider;
|
class i_provider;
|
||||||
@ -68,19 +69,19 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool allocated{false};
|
bool allocated{false};
|
||||||
|
std::unique_ptr<utils::file::i_file> nf_;
|
||||||
bool notified_{false};
|
bool notified_{false};
|
||||||
std::size_t read_chunk_{};
|
std::size_t read_chunk_{};
|
||||||
boost::dynamic_bitset<> read_state_;
|
boost::dynamic_bitset<> read_state_;
|
||||||
std::unique_ptr<std::thread> reader_thread_;
|
std::unique_ptr<std::thread> reader_thread_;
|
||||||
std::unique_ptr<std::thread> download_thread_;
|
|
||||||
mutable std::recursive_mutex rw_mtx_;
|
mutable std::recursive_mutex rw_mtx_;
|
||||||
stop_type stop_requested_{false};
|
stop_type stop_requested_{false};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] auto adjust_cache_size(std::uint64_t file_size, bool shrink)
|
[[nodiscard]] auto adjust_cache_size(std::uint64_t file_size,
|
||||||
-> api_error;
|
bool shrink) -> api_error;
|
||||||
|
|
||||||
[[nodiscard]] auto check_allocation() -> api_error;
|
[[nodiscard]] auto check_start() -> api_error;
|
||||||
|
|
||||||
void download_chunk(std::size_t chunk, bool skip_active, bool should_reset);
|
void download_chunk(std::size_t chunk, bool skip_active, bool should_reset);
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ private:
|
|||||||
|
|
||||||
void set_read_state(boost::dynamic_bitset<> read_state);
|
void set_read_state(boost::dynamic_bitset<> read_state);
|
||||||
|
|
||||||
void update_background_reader(std::size_t read_chunk);
|
void update_reader(std::size_t chunk);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
auto close() -> bool override;
|
auto close() -> bool override;
|
||||||
@ -110,12 +111,12 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto native_operation(native_operation_callback callback)
|
[[nodiscard]] auto
|
||||||
-> api_error override;
|
native_operation(native_operation_callback callback) -> api_error override;
|
||||||
|
|
||||||
[[nodiscard]] auto native_operation(std::uint64_t new_file_size,
|
[[nodiscard]] auto
|
||||||
native_operation_callback callback)
|
native_operation(std::uint64_t new_file_size,
|
||||||
-> api_error override;
|
native_operation_callback callback) -> api_error override;
|
||||||
|
|
||||||
void remove(std::uint64_t handle) override;
|
void remove(std::uint64_t handle) override;
|
||||||
|
|
||||||
|
@ -24,21 +24,18 @@
|
|||||||
|
|
||||||
#include "file_manager/i_open_file.hpp"
|
#include "file_manager/i_open_file.hpp"
|
||||||
|
|
||||||
#include "utils/types/file/i_file.hpp"
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
class i_provider;
|
class i_provider;
|
||||||
|
|
||||||
class open_file_base : public i_closeable_open_file {
|
class open_file_base : public i_closeable_open_file {
|
||||||
public:
|
public:
|
||||||
open_file_base(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
open_file_base(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
||||||
filesystem_item fsi, i_provider &provider,
|
filesystem_item fsi, i_provider &provider, bool disable_io);
|
||||||
bool disable_io = false);
|
|
||||||
|
|
||||||
open_file_base(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
open_file_base(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
||||||
filesystem_item fsi,
|
filesystem_item fsi,
|
||||||
std::map<std::uint64_t, open_file_data> open_data,
|
std::map<std::uint64_t, open_file_data> open_data,
|
||||||
i_provider &provider, bool disable_io = false);
|
i_provider &provider, bool disable_io);
|
||||||
|
|
||||||
~open_file_base() override = default;
|
~open_file_base() override = default;
|
||||||
|
|
||||||
@ -99,7 +96,7 @@ public:
|
|||||||
[[nodiscard]] auto get_result() -> api_error;
|
[[nodiscard]] auto get_result() -> api_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
std::uint64_t chunk_size_;
|
std::uint64_t chunk_size_;
|
||||||
std::uint8_t chunk_timeout_;
|
std::uint8_t chunk_timeout_;
|
||||||
filesystem_item fsi_;
|
filesystem_item fsi_;
|
||||||
@ -108,21 +105,19 @@ protected:
|
|||||||
i_provider &provider_;
|
i_provider &provider_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unordered_map<std::size_t, std::shared_ptr<download>> active_downloads_;
|
||||||
api_error error_{api_error::success};
|
api_error error_{api_error::success};
|
||||||
mutable std::mutex error_mtx_;
|
mutable std::mutex error_mtx_;
|
||||||
|
mutable std::recursive_mutex file_mtx_;
|
||||||
stop_type io_stop_requested_{false};
|
stop_type io_stop_requested_{false};
|
||||||
std::unique_ptr<std::thread> io_thread_;
|
std::unique_ptr<std::thread> io_thread_;
|
||||||
|
|
||||||
protected:
|
|
||||||
std::unordered_map<std::size_t, std::shared_ptr<download>> active_downloads_;
|
|
||||||
mutable std::recursive_mutex file_mtx_;
|
|
||||||
std::atomic<std::chrono::system_clock::time_point> last_access_{
|
|
||||||
std::chrono::system_clock::now()};
|
|
||||||
bool modified_{false};
|
|
||||||
std::unique_ptr<utils::file::i_file> nf_;
|
|
||||||
mutable std::mutex io_thread_mtx_;
|
mutable std::mutex io_thread_mtx_;
|
||||||
std::condition_variable io_thread_notify_;
|
std::condition_variable io_thread_notify_;
|
||||||
std::deque<std::shared_ptr<io_item>> io_thread_queue_;
|
std::deque<std::shared_ptr<io_item>> io_thread_queue_;
|
||||||
|
std::atomic<std::chrono::system_clock::time_point> last_access_{
|
||||||
|
std::chrono::system_clock::now(),
|
||||||
|
};
|
||||||
|
bool modified_{false};
|
||||||
bool removed_{false};
|
bool removed_{false};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -131,9 +126,42 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
[[nodiscard]] auto do_io(std::function<api_error()> action) -> api_error;
|
[[nodiscard]] auto do_io(std::function<api_error()> action) -> api_error;
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_active_downloads()
|
||||||
|
-> std::unordered_map<std::size_t, std::shared_ptr<download>> & {
|
||||||
|
return active_downloads_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_mutex() const -> std::recursive_mutex & {
|
||||||
|
return file_mtx_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_last_chunk_size() const -> std::size_t;
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_provider() -> i_provider & { return provider_; }
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_provider() const -> const i_provider & {
|
||||||
|
return provider_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto is_removed() const -> bool;
|
||||||
|
|
||||||
|
void notify_io();
|
||||||
|
|
||||||
void reset_timeout();
|
void reset_timeout();
|
||||||
|
|
||||||
auto set_api_error(const api_error &e) -> api_error;
|
auto set_api_error(const api_error &err) -> api_error;
|
||||||
|
|
||||||
|
void set_file_size(std::uint64_t size);
|
||||||
|
|
||||||
|
void set_last_chunk_size(std::size_t size);
|
||||||
|
|
||||||
|
void set_modified(bool modified);
|
||||||
|
|
||||||
|
void set_removed(bool removed);
|
||||||
|
|
||||||
|
void set_source_path(std::string source_path);
|
||||||
|
|
||||||
|
void wait_for_io(stop_type &stop_requested);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void add(std::uint64_t handle, open_file_data ofd) override;
|
void add(std::uint64_t handle, open_file_data ofd) override;
|
||||||
@ -158,23 +186,21 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] auto get_handles() const -> std::vector<std::uint64_t> override;
|
[[nodiscard]] auto get_handles() const -> std::vector<std::uint64_t> override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_open_data()
|
[[nodiscard]] auto
|
||||||
-> std::map<std::uint64_t, open_file_data> & override;
|
get_open_data() -> std::map<std::uint64_t, open_file_data> & override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_open_data() const
|
[[nodiscard]] auto get_open_data() const
|
||||||
-> const std::map<std::uint64_t, open_file_data> & override;
|
-> const std::map<std::uint64_t, open_file_data> & override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_open_data(std::uint64_t handle)
|
[[nodiscard]] auto
|
||||||
-> open_file_data & override;
|
get_open_data(std::uint64_t handle) -> open_file_data & override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_open_data(std::uint64_t handle) const
|
[[nodiscard]] auto
|
||||||
-> const open_file_data & override;
|
get_open_data(std::uint64_t handle) const -> const open_file_data & override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_open_file_count() const -> std::size_t override;
|
[[nodiscard]] auto get_open_file_count() const -> std::size_t override;
|
||||||
|
|
||||||
[[nodiscard]] auto get_source_path() const -> std::string override {
|
[[nodiscard]] auto get_source_path() const -> std::string override;
|
||||||
return fsi_.source_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto has_handle(std::uint64_t handle) const -> bool override;
|
[[nodiscard]] auto has_handle(std::uint64_t handle) const -> bool override;
|
||||||
|
|
||||||
|
150
repertory/librepertory/include/file_manager/ring_buffer_base.hpp
Normal file
150
repertory/librepertory/include/file_manager/ring_buffer_base.hpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_BASE_HPP_
|
||||||
|
#define REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_BASE_HPP_
|
||||||
|
|
||||||
|
#include "file_manager/open_file_base.hpp"
|
||||||
|
|
||||||
|
#include "types/repertory.hpp"
|
||||||
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
|
namespace repertory {
|
||||||
|
class i_provider;
|
||||||
|
class i_upload_manager;
|
||||||
|
|
||||||
|
class ring_buffer_base : public open_file_base {
|
||||||
|
public:
|
||||||
|
ring_buffer_base(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
||||||
|
filesystem_item fsi, i_provider &provider,
|
||||||
|
std::size_t ring_size, bool disable_io);
|
||||||
|
|
||||||
|
~ring_buffer_base() override = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ring_buffer_base() = delete;
|
||||||
|
ring_buffer_base(const ring_buffer_base &) noexcept = delete;
|
||||||
|
ring_buffer_base(ring_buffer_base &&) noexcept = delete;
|
||||||
|
auto operator=(ring_buffer_base &&) noexcept -> ring_buffer_base & = delete;
|
||||||
|
auto
|
||||||
|
operator=(const ring_buffer_base &) noexcept -> ring_buffer_base & = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const auto min_ring_size{5U};
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::dynamic_bitset<> read_state_;
|
||||||
|
std::size_t total_chunks_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::condition_variable chunk_notify_;
|
||||||
|
mutable std::mutex chunk_mtx_;
|
||||||
|
std::mutex read_mtx_;
|
||||||
|
std::unique_ptr<std::thread> reader_thread_;
|
||||||
|
std::size_t ring_begin_{};
|
||||||
|
std::size_t ring_end_{};
|
||||||
|
std::size_t ring_pos_{};
|
||||||
|
stop_type stop_requested_{false};
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] auto check_start() -> api_error;
|
||||||
|
|
||||||
|
auto download_chunk(std::size_t chunk, bool skip_active) -> api_error;
|
||||||
|
|
||||||
|
void reader_thread();
|
||||||
|
|
||||||
|
void update_position(std::size_t count, bool is_forward);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
[[nodiscard]] auto has_reader_thread() const -> bool {
|
||||||
|
return reader_thread_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_ring_size() const -> std::size_t {
|
||||||
|
return read_state_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto on_check_start() -> bool = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto
|
||||||
|
on_chunk_downloaded(std::size_t chunk,
|
||||||
|
const data_buffer &buffer) -> api_error = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto
|
||||||
|
on_read_chunk(std::size_t chunk, std::size_t read_size,
|
||||||
|
std::uint64_t read_offset, data_buffer &data,
|
||||||
|
std::size_t &bytes_read) -> api_error = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto
|
||||||
|
use_buffer(std::size_t chunk,
|
||||||
|
std::function<api_error(data_buffer &)> func) -> api_error = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto close() -> bool override;
|
||||||
|
|
||||||
|
void forward(std::size_t count);
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_current_chunk() const -> std::size_t {
|
||||||
|
return ring_pos_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_first_chunk() const -> std::size_t {
|
||||||
|
return ring_begin_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_last_chunk() const -> std::size_t { return ring_end_; }
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_read_state() const -> boost::dynamic_bitset<> override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_read_state(std::size_t chunk) const -> bool override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto get_total_chunks() const -> std::size_t {
|
||||||
|
return total_chunks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto is_complete() const -> bool override { return false; }
|
||||||
|
|
||||||
|
[[nodiscard]] auto is_write_supported() const -> bool override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto read(std::size_t read_size, std::uint64_t read_offset,
|
||||||
|
data_buffer &data) -> api_error override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto resize(std::uint64_t /* size */) -> api_error override {
|
||||||
|
return api_error::not_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse(std::size_t count);
|
||||||
|
|
||||||
|
void set(std::size_t first_chunk, std::size_t current_chunk);
|
||||||
|
|
||||||
|
void set_api_path(const std::string &api_path) override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
write(std::uint64_t /* write_offset */, const data_buffer & /* data */,
|
||||||
|
std::size_t & /* bytes_written */) -> api_error override {
|
||||||
|
return api_error::not_supported;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace repertory
|
||||||
|
|
||||||
|
#endif // REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_BASE_HPP_
|
@ -22,15 +22,16 @@
|
|||||||
#ifndef REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_OPEN_FILE_HPP_
|
#ifndef REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_OPEN_FILE_HPP_
|
||||||
#define REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_OPEN_FILE_HPP_
|
#define REPERTORY_INCLUDE_FILE_MANAGER_RING_BUFFER_OPEN_FILE_HPP_
|
||||||
|
|
||||||
#include "file_manager/open_file_base.hpp"
|
#include "file_manager/ring_buffer_base.hpp"
|
||||||
|
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
|
#include "utils/file.hpp"
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
class i_provider;
|
class i_provider;
|
||||||
class i_upload_manager;
|
class i_upload_manager;
|
||||||
|
|
||||||
class ring_buffer_open_file final : public open_file_base {
|
class ring_buffer_open_file final : public ring_buffer_base {
|
||||||
public:
|
public:
|
||||||
ring_buffer_open_file(std::string buffer_directory, std::uint64_t chunk_size,
|
ring_buffer_open_file(std::string buffer_directory, std::uint64_t chunk_size,
|
||||||
std::uint8_t chunk_timeout, filesystem_item fsi,
|
std::uint8_t chunk_timeout, filesystem_item fsi,
|
||||||
@ -42,68 +43,40 @@ public:
|
|||||||
ring_buffer_open_file() = delete;
|
ring_buffer_open_file() = delete;
|
||||||
ring_buffer_open_file(const ring_buffer_open_file &) noexcept = delete;
|
ring_buffer_open_file(const ring_buffer_open_file &) noexcept = delete;
|
||||||
ring_buffer_open_file(ring_buffer_open_file &&) noexcept = delete;
|
ring_buffer_open_file(ring_buffer_open_file &&) noexcept = delete;
|
||||||
auto operator=(ring_buffer_open_file &&) noexcept
|
auto operator=(ring_buffer_open_file &&) noexcept -> ring_buffer_open_file & =
|
||||||
-> ring_buffer_open_file & = delete;
|
delete;
|
||||||
auto operator=(const ring_buffer_open_file &) noexcept
|
auto operator=(const ring_buffer_open_file &) noexcept
|
||||||
-> ring_buffer_open_file & = delete;
|
-> ring_buffer_open_file & = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::dynamic_bitset<> ring_state_;
|
|
||||||
std::size_t total_chunks_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<std::thread> chunk_forward_thread_;
|
|
||||||
std::unique_ptr<std::thread> chunk_reverse_thread_;
|
|
||||||
std::condition_variable chunk_notify_;
|
|
||||||
mutable std::mutex chunk_mtx_;
|
|
||||||
std::mutex read_mtx_;
|
|
||||||
std::unique_ptr<std::thread> reader_thread_;
|
|
||||||
std::size_t ring_begin_{};
|
|
||||||
std::size_t ring_end_{};
|
|
||||||
std::size_t ring_pos_{};
|
|
||||||
std::string source_path_;
|
std::string source_path_;
|
||||||
stop_type stop_requested_{false};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void background_reader_thread();
|
std::unique_ptr<utils::file::i_file> nf_;
|
||||||
|
|
||||||
auto download_chunk(std::size_t chunk, bool skip_active) -> api_error;
|
protected:
|
||||||
|
[[nodiscard]] auto on_check_start() -> bool override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
on_chunk_downloaded(std::size_t chunk,
|
||||||
|
const data_buffer &buffer) -> api_error override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
on_read_chunk(std::size_t chunk, std::size_t read_size,
|
||||||
|
std::uint64_t read_offset, data_buffer &data,
|
||||||
|
std::size_t &bytes_read) -> api_error override;
|
||||||
|
|
||||||
|
[[nodiscard]] auto use_buffer(std::size_t chunk,
|
||||||
|
std::function<api_error(data_buffer &)> func)
|
||||||
|
-> api_error override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static auto can_handle_file(std::uint64_t file_size,
|
[[nodiscard]] static auto can_handle_file(std::uint64_t file_size,
|
||||||
std::size_t chunk_size,
|
std::size_t chunk_size,
|
||||||
std::size_t ring_size) -> bool;
|
std::size_t ring_size) -> bool;
|
||||||
|
|
||||||
auto close() -> bool override;
|
[[nodiscard]] auto
|
||||||
|
native_operation(native_operation_callback callback) -> api_error override;
|
||||||
void forward(std::size_t count);
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_current_chunk() const -> std::size_t {
|
|
||||||
return ring_pos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_first_chunk() const -> std::size_t {
|
|
||||||
return ring_begin_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_last_chunk() const -> std::size_t { return ring_end_; }
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_read_state() const -> boost::dynamic_bitset<> override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_read_state(std::size_t chunk) const -> bool override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto get_total_chunks() const -> std::size_t {
|
|
||||||
return total_chunks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto is_complete() const -> bool override { return false; }
|
|
||||||
|
|
||||||
[[nodiscard]] auto is_write_supported() const -> bool override {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto native_operation(native_operation_callback callback)
|
|
||||||
-> api_error override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto native_operation(std::uint64_t /* new_file_size */,
|
[[nodiscard]] auto native_operation(std::uint64_t /* new_file_size */,
|
||||||
native_operation_callback /* callback */)
|
native_operation_callback /* callback */)
|
||||||
@ -111,24 +84,8 @@ public:
|
|||||||
return api_error::not_supported;
|
return api_error::not_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto read(std::size_t read_size, std::uint64_t read_offset,
|
[[nodiscard]] auto get_source_path() const -> std::string override {
|
||||||
data_buffer &data) -> api_error override;
|
return source_path_;
|
||||||
|
|
||||||
[[nodiscard]] auto resize(std::uint64_t /* size */) -> api_error override {
|
|
||||||
return api_error::not_supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reverse(std::size_t count);
|
|
||||||
|
|
||||||
void set(std::size_t first_chunk, std::size_t current_chunk);
|
|
||||||
|
|
||||||
void set_api_path(const std::string &api_path) override;
|
|
||||||
|
|
||||||
[[nodiscard]] auto write(std::uint64_t /* write_offset */,
|
|
||||||
const data_buffer & /* data */,
|
|
||||||
std::size_t & /* bytes_written */)
|
|
||||||
-> api_error override {
|
|
||||||
return api_error::not_supported;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
@ -81,8 +81,8 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
|
|||||||
struct fuse_file_info * /*file_info*/)
|
struct fuse_file_info * /*file_info*/)
|
||||||
-> api_error {
|
-> api_error {
|
||||||
#else
|
#else
|
||||||
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
|
auto fuse_drive::chown_impl(std::string api_path, uid_t uid,
|
||||||
-> api_error {
|
gid_t gid) -> api_error {
|
||||||
#endif
|
#endif
|
||||||
return check_and_perform(
|
return check_and_perform(
|
||||||
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
||||||
@ -481,8 +481,8 @@ auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st,
|
|||||||
struct fuse_file_info * /*file_info*/)
|
struct fuse_file_info * /*file_info*/)
|
||||||
-> api_error {
|
-> api_error {
|
||||||
#else
|
#else
|
||||||
auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st)
|
auto fuse_drive::getattr_impl(std::string api_path,
|
||||||
-> api_error {
|
struct stat *unix_st) -> api_error {
|
||||||
#endif
|
#endif
|
||||||
auto parent = utils::path::get_parent_api_path(api_path);
|
auto parent = utils::path::get_parent_api_path(api_path);
|
||||||
|
|
||||||
@ -565,8 +565,8 @@ auto fuse_drive::getxtimes_impl(std::string api_path, struct timespec *bkuptime,
|
|||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
|
||||||
#if FUSE_USE_VERSION >= 30
|
#if FUSE_USE_VERSION >= 30
|
||||||
auto fuse_drive::init_impl(struct fuse_conn_info *conn, struct fuse_config *cfg)
|
auto fuse_drive::init_impl(struct fuse_conn_info *conn,
|
||||||
-> void * {
|
struct fuse_config *cfg) -> void * {
|
||||||
#else
|
#else
|
||||||
void *fuse_drive::init_impl(struct fuse_conn_info *conn) {
|
void *fuse_drive::init_impl(struct fuse_conn_info *conn) {
|
||||||
#endif
|
#endif
|
||||||
@ -800,9 +800,8 @@ auto fuse_drive::release_impl(std::string /*api_path*/,
|
|||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fuse_drive::releasedir_impl(std::string /*api_path*/,
|
auto fuse_drive::releasedir_impl(
|
||||||
struct fuse_file_info *file_info)
|
std::string /*api_path*/, struct fuse_file_info *file_info) -> api_error {
|
||||||
-> api_error {
|
|
||||||
auto iter = directory_cache_->get_directory(file_info->fh);
|
auto iter = directory_cache_->get_directory(file_info->fh);
|
||||||
if (iter == nullptr) {
|
if (iter == nullptr) {
|
||||||
return api_error::invalid_handle;
|
return api_error::invalid_handle;
|
||||||
@ -820,8 +819,8 @@ auto fuse_drive::rename_directory(const std::string &from_api_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto fuse_drive::rename_file(const std::string &from_api_path,
|
auto fuse_drive::rename_file(const std::string &from_api_path,
|
||||||
const std::string &to_api_path, bool overwrite)
|
const std::string &to_api_path,
|
||||||
-> int {
|
bool overwrite) -> int {
|
||||||
auto res = fm_->rename_file(from_api_path, to_api_path, overwrite);
|
auto res = fm_->rename_file(from_api_path, to_api_path, overwrite);
|
||||||
errno = std::abs(utils::from_api_error(res));
|
errno = std::abs(utils::from_api_error(res));
|
||||||
return (res == api_error::success) ? 0 : -1;
|
return (res == api_error::success) ? 0 : -1;
|
||||||
@ -831,8 +830,8 @@ auto fuse_drive::rename_file(const std::string &from_api_path,
|
|||||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path,
|
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path,
|
||||||
unsigned int /*flags*/) -> api_error {
|
unsigned int /*flags*/) -> api_error {
|
||||||
#else
|
#else
|
||||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path)
|
auto fuse_drive::rename_impl(std::string from_api_path,
|
||||||
-> api_error {
|
std::string to_api_path) -> api_error {
|
||||||
#endif
|
#endif
|
||||||
auto res = check_parent_access(to_api_path, W_OK | X_OK);
|
auto res = check_parent_access(to_api_path, W_OK | X_OK);
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
@ -946,15 +945,15 @@ auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
|
|||||||
}
|
}
|
||||||
#else // __APPLE__
|
#else // __APPLE__
|
||||||
auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
|
auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
|
||||||
char *value, size_t size, int &attribute_size)
|
char *value, size_t size,
|
||||||
-> api_error {
|
int &attribute_size) -> api_error {
|
||||||
return getxattr_common(api_path, name, value, size, attribute_size, nullptr);
|
return getxattr_common(api_path, name, value, size, attribute_size, nullptr);
|
||||||
}
|
}
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
|
||||||
auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
|
auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
|
||||||
int &required_size, bool &return_size)
|
int &required_size,
|
||||||
-> api_error {
|
bool &return_size) -> api_error {
|
||||||
auto check_size = (size == 0);
|
auto check_size = (size == 0);
|
||||||
|
|
||||||
auto res = check_parent_access(api_path, X_OK);
|
auto res = check_parent_access(api_path, X_OK);
|
||||||
@ -994,8 +993,8 @@ auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fuse_drive::removexattr_impl(std::string api_path, const char *name)
|
auto fuse_drive::removexattr_impl(std::string api_path,
|
||||||
-> api_error {
|
const char *name) -> api_error {
|
||||||
std::string attribute_name;
|
std::string attribute_name;
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
auto res = parse_xattr_parameters(name, 0, attribute_name, api_path);
|
auto res = parse_xattr_parameters(name, 0, attribute_name, api_path);
|
||||||
@ -1023,8 +1022,8 @@ auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
|
|||||||
uint32_t position) -> api_error {
|
uint32_t position) -> api_error {
|
||||||
#else // __APPLE__
|
#else // __APPLE__
|
||||||
auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
|
auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
|
||||||
const char *value, size_t size, int flags)
|
const char *value, size_t size,
|
||||||
-> api_error {
|
int flags) -> api_error {
|
||||||
#endif
|
#endif
|
||||||
std::string attribute_name;
|
std::string attribute_name;
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
@ -1102,8 +1101,8 @@ void fuse_drive::set_item_meta(const std::string &api_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
|
auto fuse_drive::setattr_x_impl(std::string api_path,
|
||||||
-> api_error {
|
struct setattr_x *attr) -> api_error {
|
||||||
bool exists{};
|
bool exists{};
|
||||||
auto res = provider_.is_file(api_path, exists);
|
auto res = provider_.is_file(api_path, exists);
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
@ -1157,7 +1156,7 @@ auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
|
|||||||
ts[0].tv_sec = attr->acctime.tv_sec;
|
ts[0].tv_sec = attr->acctime.tv_sec;
|
||||||
ts[0].tv_nsec = attr->acctime.tv_nsec;
|
ts[0].tv_nsec = attr->acctime.tv_nsec;
|
||||||
} else {
|
} else {
|
||||||
struct timeval tv{};
|
struct timeval tv {};
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
ts[0].tv_sec = tv.tv_sec;
|
ts[0].tv_sec = tv.tv_sec;
|
||||||
ts[0].tv_nsec = tv.tv_usec * 1000;
|
ts[0].tv_nsec = tv.tv_usec * 1000;
|
||||||
@ -1202,9 +1201,8 @@ auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
|
|||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fuse_drive::setbkuptime_impl(std::string api_path,
|
auto fuse_drive::setbkuptime_impl(
|
||||||
const struct timespec *bkuptime)
|
std::string api_path, const struct timespec *bkuptime) -> api_error {
|
||||||
-> api_error {
|
|
||||||
return check_and_perform(
|
return check_and_perform(
|
||||||
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
||||||
auto nanos = bkuptime->tv_nsec +
|
auto nanos = bkuptime->tv_nsec +
|
||||||
@ -1240,8 +1238,8 @@ auto fuse_drive::setvolname_impl(const char * /*volname*/) -> api_error {
|
|||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf)
|
auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
|
||||||
-> api_error {
|
struct statfs *stbuf) -> api_error {
|
||||||
if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) {
|
if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) {
|
||||||
return api_error::os_error;
|
return api_error::os_error;
|
||||||
}
|
}
|
||||||
@ -1266,8 +1264,8 @@ auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf)
|
|||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
#else // __APPLE__
|
#else // __APPLE__
|
||||||
auto fuse_drive::statfs_impl(std::string /*api_path*/, struct statvfs *stbuf)
|
auto fuse_drive::statfs_impl(std::string /*api_path*/,
|
||||||
-> api_error {
|
struct statvfs *stbuf) -> api_error {
|
||||||
if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) {
|
if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) {
|
||||||
return api_error::os_error;
|
return api_error::os_error;
|
||||||
}
|
}
|
||||||
@ -1317,6 +1315,10 @@ auto fuse_drive::truncate_impl(std::string api_path, off_t size) -> api_error {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (not fm_->get_open_file(handle, true, open_file)) {
|
||||||
|
return api_error::invalid_handle;
|
||||||
|
}
|
||||||
|
|
||||||
res = open_file->resize(static_cast<std::uint64_t>(size));
|
res = open_file->resize(static_cast<std::uint64_t>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,8 +1349,8 @@ auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2],
|
|||||||
struct fuse_file_info * /*file_info*/)
|
struct fuse_file_info * /*file_info*/)
|
||||||
-> api_error {
|
-> api_error {
|
||||||
#else
|
#else
|
||||||
auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2])
|
auto fuse_drive::utimens_impl(std::string api_path,
|
||||||
-> api_error {
|
const struct timespec tv[2]) -> api_error {
|
||||||
#endif
|
#endif
|
||||||
api_meta_map meta;
|
api_meta_map meta;
|
||||||
auto res = provider_.get_item_meta(api_path, meta);
|
auto res = provider_.get_item_meta(api_path, meta);
|
||||||
|
@ -21,299 +21,43 @@
|
|||||||
*/
|
*/
|
||||||
#include "file_manager/direct_open_file.hpp"
|
#include "file_manager/direct_open_file.hpp"
|
||||||
|
|
||||||
#include "events/event_system.hpp"
|
|
||||||
#include "file_manager/events.hpp"
|
|
||||||
#include "file_manager/open_file_base.hpp"
|
#include "file_manager/open_file_base.hpp"
|
||||||
#include "providers/i_provider.hpp"
|
#include "providers/i_provider.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "utils/common.hpp"
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
direct_open_file::direct_open_file(std::uint64_t chunk_size,
|
direct_open_file::direct_open_file(std::uint64_t chunk_size,
|
||||||
std::uint8_t chunk_timeout,
|
std::uint8_t chunk_timeout,
|
||||||
filesystem_item fsi, i_provider &provider)
|
filesystem_item fsi, i_provider &provider)
|
||||||
: open_file_base(chunk_size, chunk_timeout, fsi, provider),
|
: ring_buffer_base(chunk_size, chunk_timeout, fsi, provider,
|
||||||
ring_state_(ring_size),
|
min_ring_size, true) {}
|
||||||
total_chunks_(static_cast<std::size_t>(
|
|
||||||
utils::divide_with_ceiling(fsi_.size, chunk_size))) {
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
ring_state_.set(0U, ring_state_.size(), true);
|
|
||||||
|
|
||||||
if (fsi_.size > 0U) {
|
|
||||||
reader_thread_ =
|
|
||||||
std::make_unique<std::thread>([this]() { background_reader_thread(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
event_system::instance().raise<download_begin>(fsi_.api_path, "direct");
|
|
||||||
}
|
|
||||||
|
|
||||||
direct_open_file::~direct_open_file() {
|
direct_open_file::~direct_open_file() {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
|
||||||
if (reader_thread_) {
|
|
||||||
reader_thread_->join();
|
|
||||||
reader_thread_.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void direct_open_file::background_reader_thread() {
|
auto direct_open_file::on_check_start() -> bool {
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
return (get_file_size() == 0U || has_reader_thread());
|
||||||
auto next_chunk = ring_pos_;
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
|
|
||||||
while (not stop_requested_) {
|
|
||||||
chunk_lock.lock();
|
|
||||||
|
|
||||||
next_chunk = next_chunk + 1U > ring_end_ ? ring_begin_ : next_chunk + 1U;
|
|
||||||
const auto check_and_wait = [this, &chunk_lock, &next_chunk]() {
|
|
||||||
if (stop_requested_) {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_read_state().all()) {
|
|
||||||
chunk_notify_.wait(chunk_lock);
|
|
||||||
next_chunk = ring_pos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (not ring_state_[next_chunk % ring_state_.size()]) {
|
|
||||||
check_and_wait();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
|
|
||||||
download_chunk(next_chunk, true);
|
|
||||||
|
|
||||||
chunk_lock.lock();
|
|
||||||
check_and_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
event_system::instance().raise<download_end>(fsi_.api_path, "direct",
|
|
||||||
api_error::download_stopped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto direct_open_file::close() -> bool {
|
auto direct_open_file::on_read_chunk(std::size_t chunk, std::size_t read_size,
|
||||||
stop_requested_ = true;
|
std::uint64_t read_offset,
|
||||||
|
data_buffer &data,
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
std::size_t &bytes_read) -> api_error {
|
||||||
chunk_notify_.notify_all();
|
auto &buffer = ring_data_.at(chunk % get_ring_size());
|
||||||
chunk_lock.unlock();
|
auto begin =
|
||||||
|
std::next(buffer.begin(), static_cast<std::int64_t>(read_offset));
|
||||||
return open_file_base::close();
|
auto end = std::next(begin, static_cast<std::int64_t>(read_size));
|
||||||
|
data.insert(data.end(), begin, end);
|
||||||
|
bytes_read = read_size;
|
||||||
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto direct_open_file::download_chunk(std::size_t chunk, bool skip_active)
|
auto direct_open_file::use_buffer(std::size_t chunk,
|
||||||
|
std::function<api_error(data_buffer &)> func)
|
||||||
-> api_error {
|
-> api_error {
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
return func(ring_data_.at(chunk % get_ring_size()));
|
||||||
const auto unlock_and_notify = [this, &chunk_lock]() {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto unlock_and_return =
|
|
||||||
[&unlock_and_notify](api_error res) -> api_error {
|
|
||||||
unlock_and_notify();
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (chunk < ring_begin_ || chunk > ring_end_) {
|
|
||||||
return unlock_and_return(api_error::invalid_ring_buffer_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active_downloads_.find(chunk) != active_downloads_.end()) {
|
|
||||||
if (skip_active) {
|
|
||||||
return unlock_and_return(api_error::success);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto active_download = active_downloads_.at(chunk);
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
return active_download->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not ring_state_[chunk % ring_state_.size()]) {
|
|
||||||
return unlock_and_return(api_error::success);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto active_download{std::make_shared<download>()};
|
|
||||||
active_downloads_[chunk] = active_download;
|
|
||||||
ring_state_[chunk % ring_state_.size()] = false;
|
|
||||||
|
|
||||||
auto &buffer = ring_data_.at(chunk % ring_state_.size());
|
|
||||||
auto data_offset{chunk * chunk_size_};
|
|
||||||
auto data_size{
|
|
||||||
chunk == (total_chunks_ - 1U) ? last_chunk_size_ : chunk_size_,
|
|
||||||
};
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
auto res{
|
|
||||||
provider_.read_file_bytes(fsi_.api_path, data_size, data_offset, buffer,
|
|
||||||
stop_requested_),
|
|
||||||
};
|
|
||||||
|
|
||||||
chunk_lock.lock();
|
|
||||||
if (res == api_error::success) {
|
|
||||||
auto progress =
|
|
||||||
(static_cast<double>(chunk + 1U) / static_cast<double>(total_chunks_)) *
|
|
||||||
100.0;
|
|
||||||
event_system::instance().raise<download_progress>(fsi_.api_path, "direct",
|
|
||||||
progress);
|
|
||||||
res = (chunk >= ring_begin_ && chunk <= ring_end_)
|
|
||||||
? res
|
|
||||||
: api_error::invalid_ring_buffer_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
active_downloads_.erase(chunk);
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
active_download->notify(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void direct_open_file::forward(std::size_t count) {
|
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
if ((ring_pos_ + count) > (total_chunks_ - 1U)) {
|
|
||||||
count = (total_chunks_ - 1U) - ring_pos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ring_pos_ + count) <= ring_end_) {
|
|
||||||
ring_pos_ += count;
|
|
||||||
} else {
|
|
||||||
auto added = count - (ring_end_ - ring_pos_);
|
|
||||||
if (added >= ring_state_.size()) {
|
|
||||||
ring_state_.set(0U, ring_state_.size(), true);
|
|
||||||
ring_pos_ += count;
|
|
||||||
ring_begin_ += added;
|
|
||||||
} else {
|
|
||||||
for (std::size_t idx = 0U; idx < added; ++idx) {
|
|
||||||
ring_state_[(ring_begin_ + idx) % ring_state_.size()] = true;
|
|
||||||
}
|
|
||||||
ring_begin_ += added;
|
|
||||||
ring_pos_ += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto direct_open_file::get_read_state() const -> boost::dynamic_bitset<> {
|
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
|
||||||
auto read_state = ring_state_;
|
|
||||||
return read_state.flip();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto direct_open_file::get_read_state(std::size_t chunk) const -> bool {
|
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
|
||||||
return not ring_state_[chunk % ring_state_.size()];
|
|
||||||
}
|
|
||||||
|
|
||||||
void direct_open_file::reverse(std::size_t count) {
|
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
count = std::min(ring_pos_, count);
|
|
||||||
|
|
||||||
if ((ring_pos_ - count) >= ring_begin_) {
|
|
||||||
ring_pos_ -= count;
|
|
||||||
} else {
|
|
||||||
auto removed = count - (ring_pos_ - ring_begin_);
|
|
||||||
if (removed >= ring_state_.size()) {
|
|
||||||
ring_state_.set(0U, ring_state_.size(), true);
|
|
||||||
ring_pos_ -= count;
|
|
||||||
ring_begin_ = ring_pos_;
|
|
||||||
} else {
|
|
||||||
for (std::size_t idx = 0U; idx < removed; ++idx) {
|
|
||||||
ring_state_[(ring_end_ - idx) % ring_state_.size()] = true;
|
|
||||||
}
|
|
||||||
ring_begin_ -= removed;
|
|
||||||
ring_pos_ -= count;
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto direct_open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
|
||||||
data_buffer &data) -> api_error {
|
|
||||||
if (fsi_.directory) {
|
|
||||||
return api_error::invalid_operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
read_size = utils::calculate_read_size(fsi_.size, read_size, read_offset);
|
|
||||||
if (read_size == 0U) {
|
|
||||||
return api_error::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto begin_chunk{static_cast<std::size_t>(read_offset / chunk_size_)};
|
|
||||||
read_offset = read_offset - (begin_chunk * chunk_size_);
|
|
||||||
|
|
||||||
auto res{api_error::success};
|
|
||||||
|
|
||||||
unique_mutex_lock read_lock(read_mtx_);
|
|
||||||
for (std::size_t chunk = begin_chunk;
|
|
||||||
not stop_requested_ && (res == api_error::success) && (read_size > 0U);
|
|
||||||
++chunk) {
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
if (chunk > ring_pos_) {
|
|
||||||
forward(chunk - ring_pos_);
|
|
||||||
} else if (chunk < ring_pos_) {
|
|
||||||
reverse(ring_pos_ - chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = download_chunk(chunk, false);
|
|
||||||
if (res != api_error::success) {
|
|
||||||
if (not stop_requested_ &&
|
|
||||||
res == api_error::invalid_ring_buffer_position) {
|
|
||||||
read_lock.unlock();
|
|
||||||
|
|
||||||
// TODO limit retry
|
|
||||||
return read(read_size, read_offset, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
auto to_read{
|
|
||||||
std::min(static_cast<std::size_t>(chunk_size_ - read_offset),
|
|
||||||
read_size),
|
|
||||||
};
|
|
||||||
|
|
||||||
auto &buffer = ring_data_.at(chunk % ring_state_.size());
|
|
||||||
auto begin =
|
|
||||||
std::next(buffer.begin(), static_cast<std::int64_t>(read_offset));
|
|
||||||
auto end = std::next(begin, static_cast<std::int64_t>(to_read));
|
|
||||||
data.insert(data.end(), begin, end);
|
|
||||||
|
|
||||||
read_offset = 0U;
|
|
||||||
read_size -= static_cast<std::uint64_t>(std::distance(begin, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
return stop_requested_ ? api_error::download_stopped : res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void direct_open_file::set_api_path(const std::string &api_path) {
|
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
open_file_base::set_api_path(api_path);
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
}
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
#include "utils/path.hpp"
|
#include "utils/path.hpp"
|
||||||
#include "utils/polling.hpp"
|
#include "utils/polling.hpp"
|
||||||
#include "utils/time.hpp"
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
file_manager::file_manager(app_config &config, i_provider &provider)
|
file_manager::file_manager(app_config &config, i_provider &provider)
|
||||||
@ -220,7 +219,7 @@ auto file_manager::get_open_file_by_handle(std::uint64_t handle) const
|
|||||||
-> std::shared_ptr<i_closeable_open_file> {
|
-> std::shared_ptr<i_closeable_open_file> {
|
||||||
auto file_iter =
|
auto file_iter =
|
||||||
std::find_if(open_file_lookup_.begin(), open_file_lookup_.end(),
|
std::find_if(open_file_lookup_.begin(), open_file_lookup_.end(),
|
||||||
[&handle](const auto &item) -> bool {
|
[&handle](auto &&item) -> bool {
|
||||||
return item.second->has_handle(handle);
|
return item.second->has_handle(handle);
|
||||||
});
|
});
|
||||||
return (file_iter == open_file_lookup_.end()) ? nullptr : file_iter->second;
|
return (file_iter == open_file_lookup_.end()) ? nullptr : file_iter->second;
|
||||||
@ -381,11 +380,10 @@ auto file_manager::open(const std::string &api_path, bool directory,
|
|||||||
return open(api_path, directory, ofd, handle, file, nullptr);
|
return open(api_path, directory, ofd, handle, file, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file_manager::open(const std::string &api_path, bool directory,
|
auto file_manager::open(
|
||||||
const open_file_data &ofd, std::uint64_t &handle,
|
const std::string &api_path, bool directory, const open_file_data &ofd,
|
||||||
std::shared_ptr<i_open_file> &file,
|
std::uint64_t &handle, std::shared_ptr<i_open_file> &file,
|
||||||
std::shared_ptr<i_closeable_open_file> closeable_file)
|
std::shared_ptr<i_closeable_open_file> closeable_file) -> api_error {
|
||||||
-> api_error {
|
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
const auto create_and_add_handle =
|
const auto create_and_add_handle =
|
||||||
@ -437,7 +435,7 @@ auto file_manager::open(const std::string &api_path, bool directory,
|
|||||||
auto ring_size{ring_buffer_file_size / chunk_size};
|
auto ring_size{ring_buffer_file_size / chunk_size};
|
||||||
|
|
||||||
const auto get_download_type = [&](download_type type) -> download_type {
|
const auto get_download_type = [&](download_type type) -> download_type {
|
||||||
if (directory || fsi.size == 0U) {
|
if (directory || fsi.size == 0U || is_processing(api_path)) {
|
||||||
return download_type::default_;
|
return download_type::default_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,8 +747,8 @@ auto file_manager::rename_directory(const std::string &from_api_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto file_manager::rename_file(const std::string &from_api_path,
|
auto file_manager::rename_file(const std::string &from_api_path,
|
||||||
const std::string &to_api_path, bool overwrite)
|
const std::string &to_api_path,
|
||||||
-> api_error {
|
bool overwrite) -> api_error {
|
||||||
if (not provider_.is_rename_supported()) {
|
if (not provider_.is_rename_supported()) {
|
||||||
return api_error::not_implemented;
|
return api_error::not_implemented;
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,10 @@
|
|||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
#include "providers/i_provider.hpp"
|
#include "providers/i_provider.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "types/startup_exception.hpp"
|
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
#include "utils/file_utils.hpp"
|
|
||||||
#include "utils/path.hpp"
|
#include "utils/path.hpp"
|
||||||
#include "utils/time.hpp"
|
#include "utils/time.hpp"
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
||||||
@ -63,11 +60,12 @@ open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
|||||||
i_provider &provider,
|
i_provider &provider,
|
||||||
std::optional<boost::dynamic_bitset<>> read_state,
|
std::optional<boost::dynamic_bitset<>> read_state,
|
||||||
i_upload_manager &mgr)
|
i_upload_manager &mgr)
|
||||||
: open_file_base(chunk_size, chunk_timeout, fsi, open_data, provider),
|
: open_file_base(chunk_size, chunk_timeout, fsi, open_data, provider,
|
||||||
|
false),
|
||||||
mgr_(mgr) {
|
mgr_(mgr) {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
if (fsi_.directory) {
|
if (fsi.directory) {
|
||||||
if (read_state.has_value()) {
|
if (read_state.has_value()) {
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi.api_path, fsi.source_path,
|
function_name, fsi.api_path, fsi.source_path,
|
||||||
@ -78,7 +76,7 @@ open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nf_ = utils::file::file::open_or_create_file(fsi.source_path,
|
nf_ = utils::file::file::open_or_create_file(fsi.source_path,
|
||||||
provider_.is_read_only());
|
get_provider().is_read_only());
|
||||||
set_api_error(*nf_ ? api_error::success : api_error::os_error);
|
set_api_error(*nf_ ? api_error::success : api_error::os_error);
|
||||||
if (get_api_error() != api_error::success) {
|
if (get_api_error() != api_error::success) {
|
||||||
return;
|
return;
|
||||||
@ -91,12 +89,12 @@ open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsi_.size == 0U) {
|
if (fsi.size == 0U) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_state_.resize(static_cast<std::size_t>(
|
read_state_.resize(static_cast<std::size_t>(
|
||||||
utils::divide_with_ceiling(fsi_.size, chunk_size)),
|
utils::divide_with_ceiling(fsi.size, chunk_size)),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
auto file_size = nf_->size();
|
auto file_size = nf_->size();
|
||||||
@ -108,7 +106,7 @@ open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider_.is_read_only() || file_size.value() == fsi.size) {
|
if (get_provider().is_read_only() || file_size.value() == fsi.size) {
|
||||||
read_state_.set(0U, read_state_.size(), true);
|
read_state_.set(0U, read_state_.size(), true);
|
||||||
allocated = true;
|
allocated = true;
|
||||||
}
|
}
|
||||||
@ -120,16 +118,16 @@ open_file::open_file(std::uint64_t chunk_size, std::uint8_t chunk_timeout,
|
|||||||
|
|
||||||
open_file::~open_file() { close(); }
|
open_file::~open_file() { close(); }
|
||||||
|
|
||||||
auto open_file::adjust_cache_size(std::uint64_t file_size, bool shrink)
|
auto open_file::adjust_cache_size(std::uint64_t file_size,
|
||||||
-> api_error {
|
bool shrink) -> api_error {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
if (file_size == fsi_.size) {
|
if (file_size == get_file_size()) {
|
||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_size > fsi_.size) {
|
if (file_size > get_file_size()) {
|
||||||
auto size = file_size - fsi_.size;
|
auto size = file_size - get_file_size();
|
||||||
auto res = shrink ? cache_size_mgr::instance().shrink(size)
|
auto res = shrink ? cache_size_mgr::instance().shrink(size)
|
||||||
: cache_size_mgr::instance().expand(size);
|
: cache_size_mgr::instance().expand(size);
|
||||||
if (res == api_error::success) {
|
if (res == api_error::success) {
|
||||||
@ -137,13 +135,13 @@ auto open_file::adjust_cache_size(std::uint64_t file_size, bool shrink)
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi_.api_path, fsi_.source_path, res,
|
function_name, get_api_path(), get_source_path(), res,
|
||||||
fmt::format("failed to {} cache|size|{}",
|
fmt::format("failed to {} cache|size|{}",
|
||||||
(shrink ? "shrink" : "expand"), size));
|
(shrink ? "shrink" : "expand"), size));
|
||||||
return set_api_error(res);
|
return set_api_error(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = fsi_.size - file_size;
|
auto size = get_file_size() - file_size;
|
||||||
auto res = shrink ? cache_size_mgr::instance().expand(size)
|
auto res = shrink ? cache_size_mgr::instance().expand(size)
|
||||||
: cache_size_mgr::instance().shrink(size);
|
: cache_size_mgr::instance().shrink(size);
|
||||||
if (res == api_error::success) {
|
if (res == api_error::success) {
|
||||||
@ -151,16 +149,16 @@ auto open_file::adjust_cache_size(std::uint64_t file_size, bool shrink)
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi_.api_path, fsi_.source_path, res,
|
function_name, get_api_path(), get_source_path(), res,
|
||||||
fmt::format("failed to {} cache|size|{}", (shrink ? "expand" : "shrink"),
|
fmt::format("failed to {} cache|size|{}", (shrink ? "expand" : "shrink"),
|
||||||
size));
|
size));
|
||||||
return set_api_error(res);
|
return set_api_error(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto open_file::check_allocation() -> api_error {
|
auto open_file::check_start() -> api_error {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
unique_recur_mutex_lock file_lock(file_mtx_);
|
unique_recur_mutex_lock file_lock(get_mutex());
|
||||||
if (allocated) {
|
if (allocated) {
|
||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
@ -168,12 +166,12 @@ auto open_file::check_allocation() -> api_error {
|
|||||||
auto file_size = nf_->size();
|
auto file_size = nf_->size();
|
||||||
if (not file_size.has_value()) {
|
if (not file_size.has_value()) {
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi_.api_path, fsi_.source_path,
|
function_name, get_api_path(), get_source_path(),
|
||||||
utils::get_last_error_code(), "failed to get file size");
|
utils::get_last_error_code(), "failed to get file size");
|
||||||
return set_api_error(api_error::os_error);
|
return set_api_error(api_error::os_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_size.value() == fsi_.size) {
|
if (file_size.value() == get_file_size()) {
|
||||||
allocated = true;
|
allocated = true;
|
||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
@ -185,11 +183,11 @@ auto open_file::check_allocation() -> api_error {
|
|||||||
}
|
}
|
||||||
file_lock.lock();
|
file_lock.lock();
|
||||||
|
|
||||||
if (not nf_->truncate(fsi_.size)) {
|
if (not nf_->truncate(get_file_size())) {
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi_.api_path, fsi_.source_path,
|
function_name, get_api_path(), get_source_path(),
|
||||||
utils::get_last_error_code(),
|
utils::get_last_error_code(),
|
||||||
fmt::format("failed to truncate file|size|{}", fsi_.size));
|
fmt::format("failed to truncate file|size|{}", get_file_size()));
|
||||||
return set_api_error(res);
|
return set_api_error(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,15 +198,13 @@ auto open_file::check_allocation() -> api_error {
|
|||||||
auto open_file::close() -> bool {
|
auto open_file::close() -> bool {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
if (fsi_.directory || stop_requested_) {
|
if (is_directory() || stop_requested_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_requested_ = true;
|
stop_requested_ = true;
|
||||||
|
|
||||||
unique_mutex_lock reader_lock(io_thread_mtx_);
|
notify_io();
|
||||||
io_thread_notify_.notify_all();
|
|
||||||
reader_lock.unlock();
|
|
||||||
|
|
||||||
if (reader_thread_) {
|
if (reader_thread_) {
|
||||||
reader_thread_->join();
|
reader_thread_->join();
|
||||||
@ -223,9 +219,10 @@ auto open_file::close() -> bool {
|
|||||||
auto err = get_api_error();
|
auto err = get_api_error();
|
||||||
if (err == api_error::success || err == api_error::download_incomplete ||
|
if (err == api_error::success || err == api_error::download_incomplete ||
|
||||||
err == api_error::download_stopped) {
|
err == api_error::download_stopped) {
|
||||||
if (modified_ && not read_state.all()) {
|
if (is_modified() && not read_state.all()) {
|
||||||
set_api_error(api_error::download_incomplete);
|
set_api_error(api_error::download_incomplete);
|
||||||
} else if (not modified_ && (fsi_.size > 0U) && not read_state.all()) {
|
} else if (not is_modified() && (get_file_size() > 0U) &&
|
||||||
|
not read_state.all()) {
|
||||||
set_api_error(api_error::download_stopped);
|
set_api_error(api_error::download_stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +231,7 @@ auto open_file::close() -> bool {
|
|||||||
|
|
||||||
nf_->close();
|
nf_->close();
|
||||||
|
|
||||||
if (modified_) {
|
if (is_modified()) {
|
||||||
if (err == api_error::success) {
|
if (err == api_error::success) {
|
||||||
mgr_.queue_upload(*this);
|
mgr_.queue_upload(*this);
|
||||||
return true;
|
return true;
|
||||||
@ -247,24 +244,24 @@ auto open_file::close() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err != api_error::success || read_state.all()) {
|
if (err != api_error::success || read_state.all()) {
|
||||||
mgr_.remove_resume(fsi_.api_path, get_source_path());
|
mgr_.remove_resume(get_api_path(), get_source_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == api_error::success) {
|
if (err == api_error::success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_manager::remove_source_and_shrink_cache(fsi_.api_path, fsi_.source_path,
|
file_manager::remove_source_and_shrink_cache(
|
||||||
fsi_.size, allocated);
|
get_api_path(), get_source_path(), get_file_size(), allocated);
|
||||||
|
|
||||||
auto parent = utils::path::get_parent_path(fsi_.source_path);
|
auto parent = utils::path::get_parent_path(get_source_path());
|
||||||
fsi_.source_path =
|
set_source_path(utils::path::combine(parent, {utils::create_uuid_string()}));
|
||||||
utils::path::combine(parent, {utils::create_uuid_string()});
|
|
||||||
auto res =
|
auto res = get_provider().set_item_meta(get_api_path(), META_SOURCE,
|
||||||
provider_.set_item_meta(fsi_.api_path, META_SOURCE, fsi_.source_path);
|
get_source_path());
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
utils::error::raise_api_path_error(function_name, fsi_.api_path,
|
utils::error::raise_api_path_error(function_name, get_api_path(),
|
||||||
fsi_.source_path, res,
|
get_source_path(), res,
|
||||||
"failed to set new source path");
|
"failed to set new source path");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,27 +278,27 @@ void open_file::download_chunk(std::size_t chunk, bool skip_active,
|
|||||||
auto read_state = get_read_state();
|
auto read_state = get_read_state();
|
||||||
if ((get_api_error() == api_error::success) && (chunk < read_state.size()) &&
|
if ((get_api_error() == api_error::success) && (chunk < read_state.size()) &&
|
||||||
not read_state[chunk]) {
|
not read_state[chunk]) {
|
||||||
if (active_downloads_.find(chunk) != active_downloads_.end()) {
|
if (get_active_downloads().find(chunk) != get_active_downloads().end()) {
|
||||||
if (skip_active) {
|
if (skip_active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto active_download = active_downloads_.at(chunk);
|
auto active_download = get_active_downloads().at(chunk);
|
||||||
rw_lock.unlock();
|
rw_lock.unlock();
|
||||||
|
|
||||||
active_download->wait();
|
active_download->wait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data_offset = chunk * chunk_size_;
|
auto data_offset = chunk * get_chunk_size();
|
||||||
auto data_size =
|
auto data_size = (chunk == read_state.size() - 1U) ? get_last_chunk_size()
|
||||||
(chunk == read_state.size() - 1U) ? last_chunk_size_ : chunk_size_;
|
: get_chunk_size();
|
||||||
if (active_downloads_.empty() && (read_state.count() == 0U)) {
|
if (get_active_downloads().empty() && (read_state.count() == 0U)) {
|
||||||
event_system::instance().raise<download_begin>(fsi_.api_path,
|
event_system::instance().raise<download_begin>(get_api_path(),
|
||||||
fsi_.source_path);
|
get_source_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
active_downloads_[chunk] = std::make_shared<download>();
|
get_active_downloads()[chunk] = std::make_shared<download>();
|
||||||
rw_lock.unlock();
|
rw_lock.unlock();
|
||||||
|
|
||||||
if (should_reset) {
|
if (should_reset) {
|
||||||
@ -314,23 +311,23 @@ void open_file::download_chunk(std::size_t chunk, bool skip_active,
|
|||||||
auto state = get_read_state();
|
auto state = get_read_state();
|
||||||
|
|
||||||
unique_recur_mutex_lock lock(rw_mtx_);
|
unique_recur_mutex_lock lock(rw_mtx_);
|
||||||
auto active_download = active_downloads_.at(chunk);
|
auto active_download = get_active_downloads().at(chunk);
|
||||||
active_downloads_.erase(chunk);
|
get_active_downloads().erase(chunk);
|
||||||
if (get_api_error() == api_error::success) {
|
if (get_api_error() == api_error::success) {
|
||||||
auto progress = (static_cast<double>(state.count()) /
|
auto progress = (static_cast<double>(state.count()) /
|
||||||
static_cast<double>(state.size())) *
|
static_cast<double>(state.size())) *
|
||||||
100.0;
|
100.0;
|
||||||
event_system::instance().raise<download_progress>(
|
event_system::instance().raise<download_progress>(
|
||||||
fsi_.api_path, fsi_.source_path, progress);
|
get_api_path(), get_source_path(), progress);
|
||||||
if (state.all() && not notified_) {
|
if (state.all() && not notified_) {
|
||||||
notified_ = true;
|
notified_ = true;
|
||||||
event_system::instance().raise<download_end>(
|
event_system::instance().raise<download_end>(
|
||||||
fsi_.api_path, fsi_.source_path, get_api_error());
|
get_api_path(), get_source_path(), get_api_error());
|
||||||
}
|
}
|
||||||
} else if (not notified_) {
|
} else if (not notified_) {
|
||||||
notified_ = true;
|
notified_ = true;
|
||||||
event_system::instance().raise<download_end>(
|
event_system::instance().raise<download_end>(
|
||||||
fsi_.api_path, fsi_.source_path, get_api_error());
|
get_api_path(), get_source_path(), get_api_error());
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -342,7 +339,7 @@ void open_file::download_chunk(std::size_t chunk, bool skip_active,
|
|||||||
};
|
};
|
||||||
|
|
||||||
data_buffer buffer;
|
data_buffer buffer;
|
||||||
auto res = provider_.read_file_bytes(
|
auto res = get_provider().read_file_bytes(
|
||||||
get_api_path(), data_size, data_offset, buffer, stop_requested_);
|
get_api_path(), data_size, data_offset, buffer, stop_requested_);
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
set_api_error(res);
|
set_api_error(res);
|
||||||
@ -388,12 +385,12 @@ void open_file::download_range(std::size_t begin_chunk, std::size_t end_chunk,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto open_file::get_allocated() const -> bool {
|
auto open_file::get_allocated() const -> bool {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto open_file::get_read_state() const -> boost::dynamic_bitset<> {
|
auto open_file::get_read_state() const -> boost::dynamic_bitset<> {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
return read_state_;
|
return read_state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +406,7 @@ auto open_file::native_operation(
|
|||||||
return set_api_error(api_error::download_stopped);
|
return set_api_error(api_error::download_stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = check_allocation();
|
auto res = check_start();
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -423,7 +420,7 @@ auto open_file::native_operation(
|
|||||||
i_open_file::native_operation_callback callback) -> api_error {
|
i_open_file::native_operation_callback callback) -> api_error {
|
||||||
REPERTORY_USES_FUNCTION_NAME();
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
if (fsi_.directory) {
|
if (is_directory()) {
|
||||||
return set_api_error(api_error::invalid_operation);
|
return set_api_error(api_error::invalid_operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +428,7 @@ auto open_file::native_operation(
|
|||||||
return set_api_error(api_error::download_stopped);
|
return set_api_error(api_error::download_stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = check_allocation();
|
auto res = check_start();
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -445,14 +442,14 @@ auto open_file::native_operation(
|
|||||||
auto last_chunk = is_empty_file
|
auto last_chunk = is_empty_file
|
||||||
? std::size_t(0U)
|
? std::size_t(0U)
|
||||||
: static_cast<std::size_t>(utils::divide_with_ceiling(
|
: static_cast<std::size_t>(utils::divide_with_ceiling(
|
||||||
new_file_size, chunk_size_)) -
|
new_file_size, get_chunk_size())) -
|
||||||
1U;
|
1U;
|
||||||
|
|
||||||
unique_recur_mutex_lock rw_lock(rw_mtx_);
|
unique_recur_mutex_lock rw_lock(rw_mtx_);
|
||||||
auto read_state = get_read_state();
|
auto read_state = get_read_state();
|
||||||
if (not is_empty_file && (last_chunk < read_state.size())) {
|
if (not is_empty_file && (last_chunk < read_state.size())) {
|
||||||
rw_lock.unlock();
|
rw_lock.unlock();
|
||||||
update_background_reader(0U);
|
update_reader(0U);
|
||||||
|
|
||||||
download_chunk(last_chunk, false, true);
|
download_chunk(last_chunk, false, true);
|
||||||
if (get_api_error() != api_error::success) {
|
if (get_api_error() != api_error::success) {
|
||||||
@ -502,10 +499,11 @@ auto open_file::native_operation(
|
|||||||
}
|
}
|
||||||
set_read_state(read_state);
|
set_read_state(read_state);
|
||||||
|
|
||||||
last_chunk_size_ = static_cast<std::size_t>(
|
set_last_chunk_size(static_cast<std::size_t>(
|
||||||
new_file_size <= chunk_size_ ? new_file_size
|
new_file_size <= get_chunk_size() ? new_file_size
|
||||||
: (new_file_size % chunk_size_) == 0U ? chunk_size_
|
: (new_file_size % get_chunk_size()) == 0U
|
||||||
: new_file_size % chunk_size_);
|
? get_chunk_size()
|
||||||
|
: new_file_size % get_chunk_size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (original_file_size == new_file_size) {
|
if (original_file_size == new_file_size) {
|
||||||
@ -513,15 +511,15 @@ auto open_file::native_operation(
|
|||||||
}
|
}
|
||||||
set_modified();
|
set_modified();
|
||||||
|
|
||||||
fsi_.size = new_file_size;
|
set_file_size(new_file_size);
|
||||||
auto now = std::to_string(utils::time::get_time_now());
|
auto now = std::to_string(utils::time::get_time_now());
|
||||||
res = provider_.set_item_meta(fsi_.api_path,
|
res = get_provider().set_item_meta(
|
||||||
{
|
get_api_path(), {
|
||||||
{META_CHANGED, now},
|
{META_CHANGED, now},
|
||||||
{META_MODIFIED, now},
|
{META_MODIFIED, now},
|
||||||
{META_SIZE, std::to_string(new_file_size)},
|
{META_SIZE, std::to_string(new_file_size)},
|
||||||
{META_WRITTEN, now},
|
{META_WRITTEN, now},
|
||||||
});
|
});
|
||||||
if (res == api_error::success) {
|
if (res == api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -533,7 +531,7 @@ auto open_file::native_operation(
|
|||||||
|
|
||||||
auto open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
auto open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
||||||
data_buffer &data) -> api_error {
|
data_buffer &data) -> api_error {
|
||||||
if (fsi_.directory) {
|
if (is_directory()) {
|
||||||
return set_api_error(api_error::invalid_operation);
|
return set_api_error(api_error::invalid_operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,7 +545,7 @@ auto open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
|||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = check_allocation();
|
auto res = check_start();
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -555,9 +553,9 @@ auto open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
|||||||
const auto read_from_source = [this, &data, &read_offset,
|
const auto read_from_source = [this, &data, &read_offset,
|
||||||
&read_size]() -> api_error {
|
&read_size]() -> api_error {
|
||||||
return do_io([this, &data, &read_offset, &read_size]() -> api_error {
|
return do_io([this, &data, &read_offset, &read_size]() -> api_error {
|
||||||
if (provider_.is_read_only()) {
|
if (get_provider().is_read_only()) {
|
||||||
return provider_.read_file_bytes(fsi_.api_path, read_size, read_offset,
|
return get_provider().read_file_bytes(
|
||||||
data, stop_requested_);
|
get_api_path(), read_size, read_offset, data, stop_requested_);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.resize(read_size);
|
data.resize(read_size);
|
||||||
@ -573,11 +571,11 @@ auto open_file::read(std::size_t read_size, std::uint64_t read_offset,
|
|||||||
return read_from_source();
|
return read_from_source();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto begin_chunk = static_cast<std::size_t>(read_offset / chunk_size_);
|
auto begin_chunk = static_cast<std::size_t>(read_offset / get_chunk_size());
|
||||||
auto end_chunk =
|
auto end_chunk =
|
||||||
static_cast<std::size_t>((read_size + read_offset) / chunk_size_);
|
static_cast<std::size_t>((read_size + read_offset) / get_chunk_size());
|
||||||
|
|
||||||
update_background_reader(begin_chunk);
|
update_reader(begin_chunk);
|
||||||
|
|
||||||
download_range(begin_chunk, end_chunk, true);
|
download_range(begin_chunk, end_chunk, true);
|
||||||
if (get_api_error() != api_error::success) {
|
if (get_api_error() != api_error::success) {
|
||||||
@ -593,14 +591,14 @@ void open_file::remove(std::uint64_t handle) {
|
|||||||
open_file_base::remove(handle);
|
open_file_base::remove(handle);
|
||||||
|
|
||||||
recur_mutex_lock rw_lock(rw_mtx_);
|
recur_mutex_lock rw_lock(rw_mtx_);
|
||||||
if (modified_ && get_read_state().all() &&
|
if (is_modified() && get_read_state().all() &&
|
||||||
(get_api_error() == api_error::success)) {
|
(get_api_error() == api_error::success)) {
|
||||||
mgr_.queue_upload(*this);
|
mgr_.queue_upload(*this);
|
||||||
modified_ = false;
|
open_file_base::set_modified(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removed_ && (get_open_file_count() == 0U)) {
|
if (is_removed() && (get_open_file_count() == 0U)) {
|
||||||
removed_ = false;
|
open_file_base::set_removed(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,8 +606,8 @@ void open_file::remove_all() {
|
|||||||
open_file_base::remove_all();
|
open_file_base::remove_all();
|
||||||
|
|
||||||
recur_mutex_lock rw_lock(rw_mtx_);
|
recur_mutex_lock rw_lock(rw_mtx_);
|
||||||
modified_ = false;
|
open_file_base::set_modified(false);
|
||||||
removed_ = true;
|
open_file_base::set_removed(true);
|
||||||
|
|
||||||
mgr_.remove_upload(get_api_path());
|
mgr_.remove_upload(get_api_path());
|
||||||
|
|
||||||
@ -617,11 +615,11 @@ void open_file::remove_all() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto open_file::resize(std::uint64_t new_file_size) -> api_error {
|
auto open_file::resize(std::uint64_t new_file_size) -> api_error {
|
||||||
if (fsi_.directory) {
|
if (is_directory()) {
|
||||||
return set_api_error(api_error::invalid_operation);
|
return set_api_error(api_error::invalid_operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_file_size == fsi_.size) {
|
if (new_file_size == get_file_size()) {
|
||||||
return api_error::success;
|
return api_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,58 +631,56 @@ auto open_file::resize(std::uint64_t new_file_size) -> api_error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void open_file::set_modified() {
|
void open_file::set_modified() {
|
||||||
if (not modified_) {
|
if (not is_modified()) {
|
||||||
modified_ = true;
|
open_file_base::set_modified(true);
|
||||||
mgr_.store_resume(*this);
|
mgr_.store_resume(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not removed_) {
|
if (not is_removed()) {
|
||||||
removed_ = true;
|
open_file_base::set_removed(true);
|
||||||
mgr_.remove_upload(get_api_path());
|
mgr_.remove_upload(get_api_path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_file::set_read_state(std::size_t chunk) {
|
void open_file::set_read_state(std::size_t chunk) {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
read_state_.set(chunk);
|
read_state_.set(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_file::set_read_state(boost::dynamic_bitset<> read_state) {
|
void open_file::set_read_state(boost::dynamic_bitset<> read_state) {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
read_state_ = std::move(read_state);
|
read_state_ = std::move(read_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_file::update_background_reader(std::size_t read_chunk) {
|
void open_file::update_reader(std::size_t chunk) {
|
||||||
recur_mutex_lock rw_lock(rw_mtx_);
|
recur_mutex_lock rw_lock(rw_mtx_);
|
||||||
read_chunk_ = read_chunk;
|
read_chunk_ = chunk;
|
||||||
|
|
||||||
if (reader_thread_ || stop_requested_) {
|
if (reader_thread_ || stop_requested_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_thread_ = std::make_unique<std::thread>([this]() {
|
reader_thread_ = std::make_unique<std::thread>([this]() {
|
||||||
std::size_t next_chunk{};
|
unique_recur_mutex_lock lock(rw_mtx_);
|
||||||
while (not stop_requested_) {
|
auto next_chunk{read_chunk_};
|
||||||
unique_recur_mutex_lock lock(rw_mtx_);
|
auto read_chunk{read_chunk_};
|
||||||
auto read_state = get_read_state();
|
lock.unlock();
|
||||||
if ((fsi_.size == 0U) || read_state.all()) {
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
unique_mutex_lock io_lock(io_thread_mtx_);
|
while (not stop_requested_) {
|
||||||
if (not stop_requested_ && io_thread_queue_.empty()) {
|
lock.lock();
|
||||||
io_thread_notify_.wait(io_lock);
|
|
||||||
}
|
auto read_state = get_read_state();
|
||||||
io_thread_notify_.notify_all();
|
if ((get_file_size() == 0U) || read_state.all()) {
|
||||||
io_lock.unlock();
|
lock.unlock();
|
||||||
|
wait_for_io(stop_requested_);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
if (read_chunk != read_chunk_) {
|
||||||
next_chunk = read_chunk_ =
|
next_chunk = read_chunk = read_chunk_;
|
||||||
((read_chunk_ + 1U) >= read_state.size()) ? 0U : read_chunk_ + 1U;
|
}
|
||||||
} while ((next_chunk != 0U) &&
|
|
||||||
(active_downloads_.find(next_chunk) != active_downloads_.end()));
|
|
||||||
|
|
||||||
|
next_chunk = next_chunk + 1U >= read_state.size() ? 0U : next_chunk + 1U;
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
download_chunk(next_chunk, true, false);
|
download_chunk(next_chunk, true, false);
|
||||||
@ -698,7 +694,7 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data,
|
|||||||
|
|
||||||
bytes_written = 0U;
|
bytes_written = 0U;
|
||||||
|
|
||||||
if (fsi_.directory || provider_.is_read_only()) {
|
if (is_directory() || get_provider().is_read_only()) {
|
||||||
return set_api_error(api_error::invalid_operation);
|
return set_api_error(api_error::invalid_operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,16 +706,16 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data,
|
|||||||
return set_api_error(api_error::download_stopped);
|
return set_api_error(api_error::download_stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = check_allocation();
|
auto res = check_start();
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto begin_chunk = static_cast<std::size_t>(write_offset / chunk_size_);
|
auto begin_chunk = static_cast<std::size_t>(write_offset / get_chunk_size());
|
||||||
auto end_chunk =
|
auto end_chunk =
|
||||||
static_cast<std::size_t>((write_offset + data.size()) / chunk_size_);
|
static_cast<std::size_t>((write_offset + data.size()) / get_chunk_size());
|
||||||
|
|
||||||
update_background_reader(begin_chunk);
|
update_reader(begin_chunk);
|
||||||
|
|
||||||
download_range(begin_chunk, std::min(get_read_state().size() - 1U, end_chunk),
|
download_range(begin_chunk, std::min(get_read_state().size() - 1U, end_chunk),
|
||||||
true);
|
true);
|
||||||
@ -728,7 +724,7 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
unique_recur_mutex_lock rw_lock(rw_mtx_);
|
unique_recur_mutex_lock rw_lock(rw_mtx_);
|
||||||
if ((write_offset + data.size()) > fsi_.size) {
|
if ((write_offset + data.size()) > get_file_size()) {
|
||||||
res = resize(write_offset + data.size());
|
res = resize(write_offset + data.size());
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
return res;
|
return res;
|
||||||
@ -748,11 +744,11 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto now = std::to_string(utils::time::get_time_now());
|
auto now = std::to_string(utils::time::get_time_now());
|
||||||
res = provider_.set_item_meta(fsi_.api_path, {
|
res = get_provider().set_item_meta(get_api_path(), {
|
||||||
{META_CHANGED, now},
|
{META_CHANGED, now},
|
||||||
{META_MODIFIED, now},
|
{META_MODIFIED, now},
|
||||||
{META_WRITTEN, now},
|
{META_WRITTEN, now},
|
||||||
});
|
});
|
||||||
if (res != api_error::success) {
|
if (res != api_error::success) {
|
||||||
utils::error::raise_api_path_error(function_name, get_api_path(), res,
|
utils::error::raise_api_path_error(function_name, get_api_path(), res,
|
||||||
"failed to set file meta");
|
"failed to set file meta");
|
||||||
|
@ -133,6 +133,24 @@ auto open_file_base::can_close() const -> bool {
|
|||||||
return (duration.count() >= chunk_timeout_);
|
return (duration.count() >= chunk_timeout_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto open_file_base::close() -> bool {
|
||||||
|
unique_mutex_lock io_lock(io_thread_mtx_);
|
||||||
|
if (io_stop_requested_ || not io_thread_) {
|
||||||
|
io_thread_notify_.notify_all();
|
||||||
|
io_lock.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_stop_requested_ = true;
|
||||||
|
io_thread_notify_.notify_all();
|
||||||
|
io_lock.unlock();
|
||||||
|
|
||||||
|
io_thread_->join();
|
||||||
|
io_thread_.reset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto open_file_base::do_io(std::function<api_error()> action) -> api_error {
|
auto open_file_base::do_io(std::function<api_error()> action) -> api_error {
|
||||||
unique_mutex_lock io_lock(io_thread_mtx_);
|
unique_mutex_lock io_lock(io_thread_mtx_);
|
||||||
auto item = std::make_shared<io_item>(action);
|
auto item = std::make_shared<io_item>(action);
|
||||||
@ -191,6 +209,36 @@ auto open_file_base::get_file_size() const -> std::uint64_t {
|
|||||||
return fsi_.size;
|
return fsi_.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto open_file_base::get_last_chunk_size() const -> std::size_t {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
return last_chunk_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::set_file_size(std::uint64_t size) {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
fsi_.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::set_last_chunk_size(std::size_t size) {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
last_chunk_size_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::set_modified(bool modified) {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
modified_ = modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::set_removed(bool removed) {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
removed_ = removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::set_source_path(std::string source_path) {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
fsi_.source_path = std::move(source_path);
|
||||||
|
}
|
||||||
|
|
||||||
auto open_file_base::get_filesystem_item() const -> filesystem_item {
|
auto open_file_base::get_filesystem_item() const -> filesystem_item {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
return fsi_;
|
return fsi_;
|
||||||
@ -235,6 +283,11 @@ auto open_file_base::get_open_file_count() const -> std::size_t {
|
|||||||
return open_data_.size();
|
return open_data_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto open_file_base::get_source_path() const -> std::string {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
return fsi_.source_path;
|
||||||
|
}
|
||||||
|
|
||||||
auto open_file_base::has_handle(std::uint64_t handle) const -> bool {
|
auto open_file_base::has_handle(std::uint64_t handle) const -> bool {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
return open_data_.find(handle) != open_data_.end();
|
return open_data_.find(handle) != open_data_.end();
|
||||||
@ -245,6 +298,16 @@ auto open_file_base::is_modified() const -> bool {
|
|||||||
return modified_;
|
return modified_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto open_file_base::is_removed() const -> bool {
|
||||||
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
|
return removed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_base::notify_io() {
|
||||||
|
mutex_lock io_lock(io_thread_mtx_);
|
||||||
|
io_thread_notify_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
void open_file_base::remove(std::uint64_t handle) {
|
void open_file_base::remove(std::uint64_t handle) {
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
recur_mutex_lock file_lock(file_mtx_);
|
||||||
if (open_data_.find(handle) == open_data_.end()) {
|
if (open_data_.find(handle) == open_data_.end()) {
|
||||||
@ -303,21 +366,12 @@ void open_file_base::set_api_path(const std::string &api_path) {
|
|||||||
fsi_.api_parent = utils::path::get_parent_api_path(api_path);
|
fsi_.api_parent = utils::path::get_parent_api_path(api_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto open_file_base::close() -> bool {
|
void open_file_base::wait_for_io(stop_type &stop_requested) {
|
||||||
unique_mutex_lock io_lock(io_thread_mtx_);
|
unique_mutex_lock io_lock(io_thread_mtx_);
|
||||||
if (io_stop_requested_ || not io_thread_) {
|
if (not stop_requested && io_thread_queue_.empty()) {
|
||||||
io_thread_notify_.notify_all();
|
io_thread_notify_.wait(io_lock);
|
||||||
io_lock.unlock();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
io_stop_requested_ = true;
|
|
||||||
io_thread_notify_.notify_all();
|
io_thread_notify_.notify_all();
|
||||||
io_lock.unlock();
|
io_lock.unlock();
|
||||||
|
|
||||||
io_thread_->join();
|
|
||||||
io_thread_.reset();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
367
repertory/librepertory/src/file_manager/ring_buffer_base.cpp
Normal file
367
repertory/librepertory/src/file_manager/ring_buffer_base.cpp
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
/*
|
||||||
|
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "file_manager/ring_buffer_base.hpp"
|
||||||
|
|
||||||
|
#include "events/event_system.hpp"
|
||||||
|
#include "file_manager/events.hpp"
|
||||||
|
#include "file_manager/open_file_base.hpp"
|
||||||
|
#include "platform/platform.hpp"
|
||||||
|
#include "providers/i_provider.hpp"
|
||||||
|
#include "types/repertory.hpp"
|
||||||
|
#include "utils/common.hpp"
|
||||||
|
#include "utils/error_utils.hpp"
|
||||||
|
|
||||||
|
namespace repertory {
|
||||||
|
ring_buffer_base::ring_buffer_base(std::uint64_t chunk_size,
|
||||||
|
std::uint8_t chunk_timeout,
|
||||||
|
filesystem_item fsi, i_provider &provider,
|
||||||
|
std::size_t ring_size, bool disable_io)
|
||||||
|
: open_file_base(chunk_size, chunk_timeout, fsi, provider, disable_io),
|
||||||
|
read_state_(ring_size),
|
||||||
|
total_chunks_(static_cast<std::size_t>(
|
||||||
|
utils::divide_with_ceiling(fsi.size, chunk_size))) {
|
||||||
|
if (disable_io) {
|
||||||
|
if (fsi.size > 0U) {
|
||||||
|
read_state_.resize(std::min(total_chunks_, read_state_.size()));
|
||||||
|
|
||||||
|
ring_end_ =
|
||||||
|
std::min(total_chunks_ - 1U, ring_begin_ + read_state_.size() - 1U);
|
||||||
|
read_state_.set(0U, read_state_.size(), false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ring_size < min_ring_size) {
|
||||||
|
throw std::runtime_error("ring size must be greater than or equal to 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_end_ = std::min(total_chunks_ - 1U, ring_begin_ + ring_size - 1U);
|
||||||
|
read_state_.set(0U, ring_size, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::check_start() -> api_error {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (on_check_start()) {
|
||||||
|
return api_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_system::instance().raise<download_begin>(get_api_path(),
|
||||||
|
get_source_path());
|
||||||
|
reader_thread_ =
|
||||||
|
std::make_unique<std::thread>([this]() { reader_thread(); });
|
||||||
|
return api_error::success;
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
utils::error::raise_api_path_error(function_name, get_api_path(),
|
||||||
|
get_source_path(), ex,
|
||||||
|
"failed to start");
|
||||||
|
return api_error::error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::close() -> bool {
|
||||||
|
stop_requested_ = true;
|
||||||
|
|
||||||
|
unique_mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
|
||||||
|
auto res = open_file_base::close();
|
||||||
|
|
||||||
|
if (reader_thread_) {
|
||||||
|
reader_thread_->join();
|
||||||
|
reader_thread_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::download_chunk(std::size_t chunk,
|
||||||
|
bool skip_active) -> api_error {
|
||||||
|
unique_mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
const auto unlock_and_notify = [this, &chunk_lock]() {
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto unlock_and_return =
|
||||||
|
[&unlock_and_notify](api_error res) -> api_error {
|
||||||
|
unlock_and_notify();
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (chunk < ring_begin_ || chunk > ring_end_) {
|
||||||
|
return unlock_and_return(api_error::invalid_ring_buffer_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_active_downloads().find(chunk) != get_active_downloads().end()) {
|
||||||
|
if (skip_active) {
|
||||||
|
return unlock_and_return(api_error::success);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto active_download = get_active_downloads().at(chunk);
|
||||||
|
unlock_and_notify();
|
||||||
|
|
||||||
|
return active_download->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_state_[chunk % read_state_.size()]) {
|
||||||
|
return unlock_and_return(api_error::success);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto active_download{std::make_shared<download>()};
|
||||||
|
get_active_downloads()[chunk] = active_download;
|
||||||
|
|
||||||
|
return use_buffer(chunk, [&](data_buffer &buffer) -> api_error {
|
||||||
|
auto data_offset{chunk * get_chunk_size()};
|
||||||
|
auto data_size{
|
||||||
|
chunk == (total_chunks_ - 1U) ? get_last_chunk_size()
|
||||||
|
: get_chunk_size(),
|
||||||
|
};
|
||||||
|
unlock_and_notify();
|
||||||
|
|
||||||
|
auto result{
|
||||||
|
get_provider().read_file_bytes(get_api_path(), data_size, data_offset,
|
||||||
|
buffer, stop_requested_),
|
||||||
|
};
|
||||||
|
|
||||||
|
chunk_lock.lock();
|
||||||
|
if (chunk < ring_begin_ || chunk > ring_end_) {
|
||||||
|
result = api_error::invalid_ring_buffer_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == api_error::success) {
|
||||||
|
result = on_chunk_downloaded(chunk, buffer);
|
||||||
|
if (result == api_error::success) {
|
||||||
|
read_state_[chunk % read_state_.size()] = true;
|
||||||
|
auto progress = (static_cast<double>(chunk + 1U) /
|
||||||
|
static_cast<double>(total_chunks_)) *
|
||||||
|
100.0;
|
||||||
|
event_system::instance().raise<download_progress>(
|
||||||
|
get_api_path(), get_source_path(), progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_active_downloads().erase(chunk);
|
||||||
|
unlock_and_notify();
|
||||||
|
|
||||||
|
active_download->notify(result);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::forward(std::size_t count) {
|
||||||
|
update_position(count, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::get_read_state() const -> boost::dynamic_bitset<> {
|
||||||
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
|
return read_state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::get_read_state(std::size_t chunk) const -> bool {
|
||||||
|
recur_mutex_lock file_lock(get_mutex());
|
||||||
|
return read_state_[chunk % read_state_.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_base::read(std::size_t read_size, std::uint64_t read_offset,
|
||||||
|
data_buffer &data) -> api_error {
|
||||||
|
if (is_directory()) {
|
||||||
|
return api_error::invalid_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_timeout();
|
||||||
|
|
||||||
|
read_size =
|
||||||
|
utils::calculate_read_size(get_file_size(), read_size, read_offset);
|
||||||
|
if (read_size == 0U) {
|
||||||
|
return api_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin_chunk{static_cast<std::size_t>(read_offset / get_chunk_size())};
|
||||||
|
read_offset = read_offset - (begin_chunk * get_chunk_size());
|
||||||
|
|
||||||
|
unique_mutex_lock read_lock(read_mtx_);
|
||||||
|
auto res = check_start();
|
||||||
|
if (res != api_error::success) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t chunk = begin_chunk;
|
||||||
|
not stop_requested_ && (res == api_error::success) && (read_size > 0U);
|
||||||
|
++chunk) {
|
||||||
|
reset_timeout();
|
||||||
|
|
||||||
|
if (chunk > ring_pos_) {
|
||||||
|
forward(chunk - ring_pos_);
|
||||||
|
} else if (chunk < ring_pos_) {
|
||||||
|
reverse(ring_pos_ - chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = download_chunk(chunk, false);
|
||||||
|
if (res != api_error::success) {
|
||||||
|
if (res == api_error::invalid_ring_buffer_position) {
|
||||||
|
read_lock.unlock();
|
||||||
|
|
||||||
|
// TODO limit retry
|
||||||
|
return read(read_size, read_offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_timeout();
|
||||||
|
|
||||||
|
std::size_t bytes_read{};
|
||||||
|
res = on_read_chunk(
|
||||||
|
chunk,
|
||||||
|
std::min(static_cast<std::size_t>(get_chunk_size() - read_offset),
|
||||||
|
read_size),
|
||||||
|
read_offset, data, bytes_read);
|
||||||
|
if (res != api_error::success) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_timeout();
|
||||||
|
|
||||||
|
read_size -= bytes_read;
|
||||||
|
read_offset = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stop_requested_ ? api_error::download_stopped : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::reader_thread() {
|
||||||
|
unique_mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
auto next_chunk{ring_pos_};
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
|
||||||
|
while (not stop_requested_) {
|
||||||
|
chunk_lock.lock();
|
||||||
|
|
||||||
|
next_chunk = next_chunk + 1U > ring_end_ ? ring_begin_ : next_chunk + 1U;
|
||||||
|
const auto check_and_wait = [this, &chunk_lock, &next_chunk]() {
|
||||||
|
if (stop_requested_) {
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_read_state().all()) {
|
||||||
|
chunk_notify_.wait(chunk_lock);
|
||||||
|
next_chunk = ring_pos_;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (read_state_[next_chunk % read_state_.size()]) {
|
||||||
|
check_and_wait();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
chunk_lock.unlock();
|
||||||
|
|
||||||
|
download_chunk(next_chunk, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
event_system::instance().raise<download_end>(
|
||||||
|
get_api_path(), get_source_path(), api_error::download_stopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::reverse(std::size_t count) {
|
||||||
|
update_position(count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::set(std::size_t first_chunk, std::size_t current_chunk) {
|
||||||
|
mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
if (first_chunk >= total_chunks_) {
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
throw std::runtime_error("first chunk must be less than total chunks");
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_begin_ = first_chunk;
|
||||||
|
ring_end_ =
|
||||||
|
std::min(total_chunks_ - 1U, ring_begin_ + read_state_.size() - 1U);
|
||||||
|
|
||||||
|
if (current_chunk > ring_end_) {
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
throw std::runtime_error(
|
||||||
|
"current chunk must be less than or equal to last chunk");
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_pos_ = current_chunk;
|
||||||
|
read_state_.set(0U, read_state_.size(), true);
|
||||||
|
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::set_api_path(const std::string &api_path) {
|
||||||
|
mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
open_file_base::set_api_path(api_path);
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_buffer_base::update_position(std::size_t count, bool is_forward) {
|
||||||
|
mutex_lock chunk_lock(chunk_mtx_);
|
||||||
|
|
||||||
|
if (is_forward) {
|
||||||
|
if ((ring_pos_ + count) > (total_chunks_ - 1U)) {
|
||||||
|
count = (total_chunks_ - 1U) - ring_pos_;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = std::min(ring_pos_, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_forward ? (ring_pos_ + count) <= ring_end_
|
||||||
|
: (ring_pos_ - count) >= ring_begin_) {
|
||||||
|
ring_pos_ += is_forward ? count : -count;
|
||||||
|
} else {
|
||||||
|
auto delta = is_forward ? count - (ring_end_ - ring_pos_)
|
||||||
|
: count - (ring_pos_ - ring_begin_);
|
||||||
|
|
||||||
|
if (delta >= read_state_.size()) {
|
||||||
|
read_state_.set(0U, read_state_.size(), false);
|
||||||
|
ring_pos_ += is_forward ? count : -count;
|
||||||
|
ring_begin_ += is_forward ? delta : -delta;
|
||||||
|
} else {
|
||||||
|
for (std::size_t idx = 0U; idx < delta; ++idx) {
|
||||||
|
if (is_forward) {
|
||||||
|
read_state_[(ring_begin_ + idx) % read_state_.size()] = false;
|
||||||
|
} else {
|
||||||
|
read_state_[(ring_end_ - idx) % read_state_.size()] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ring_begin_ += is_forward ? delta : -delta;
|
||||||
|
ring_pos_ += is_forward ? count : -count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_end_ =
|
||||||
|
std::min(total_chunks_ - 1U, ring_begin_ + read_state_.size() - 1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_notify_.notify_all();
|
||||||
|
}
|
||||||
|
} // namespace repertory
|
@ -21,18 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
#include "file_manager/ring_buffer_open_file.hpp"
|
#include "file_manager/ring_buffer_open_file.hpp"
|
||||||
|
|
||||||
#include "app_config.hpp"
|
|
||||||
#include "events/event_system.hpp"
|
|
||||||
#include "file_manager/events.hpp"
|
|
||||||
#include "file_manager/open_file_base.hpp"
|
#include "file_manager/open_file_base.hpp"
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
#include "providers/i_provider.hpp"
|
#include "providers/i_provider.hpp"
|
||||||
#include "types/repertory.hpp"
|
#include "types/repertory.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
#include "utils/file_utils.hpp"
|
|
||||||
#include "utils/path.hpp"
|
#include "utils/path.hpp"
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
ring_buffer_open_file::ring_buffer_open_file(std::string buffer_directory,
|
ring_buffer_open_file::ring_buffer_open_file(std::string buffer_directory,
|
||||||
@ -41,45 +36,15 @@ ring_buffer_open_file::ring_buffer_open_file(std::string buffer_directory,
|
|||||||
filesystem_item fsi,
|
filesystem_item fsi,
|
||||||
i_provider &provider,
|
i_provider &provider,
|
||||||
std::size_t ring_size)
|
std::size_t ring_size)
|
||||||
: open_file_base(chunk_size, chunk_timeout, fsi, provider),
|
: ring_buffer_base(chunk_size, chunk_timeout, fsi, provider, ring_size,
|
||||||
ring_state_(ring_size),
|
false),
|
||||||
total_chunks_(static_cast<std::size_t>(
|
source_path_(utils::path::combine(buffer_directory,
|
||||||
utils::divide_with_ceiling(fsi_.size, chunk_size))) {
|
{
|
||||||
if (ring_size < 5U) {
|
utils::create_uuid_string(),
|
||||||
throw std::runtime_error("ring size must be greater than or equal to 5");
|
})) {
|
||||||
}
|
if (not can_handle_file(fsi.size, chunk_size, ring_size)) {
|
||||||
|
|
||||||
if (not can_handle_file(fsi_.size, chunk_size, ring_size)) {
|
|
||||||
throw std::runtime_error("file size is less than ring buffer size");
|
throw std::runtime_error("file size is less than ring buffer size");
|
||||||
}
|
}
|
||||||
|
|
||||||
ring_end_ = std::min(total_chunks_ - 1U, ring_begin_ + ring_size - 1U);
|
|
||||||
ring_state_.set(0U, ring_size, true);
|
|
||||||
|
|
||||||
buffer_directory = utils::path::absolute(buffer_directory);
|
|
||||||
if (not utils::file::directory(buffer_directory).create_directory()) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
fmt::format("failed to create buffer directory|path|{}|err|{}",
|
|
||||||
buffer_directory, utils::get_last_error_code()));
|
|
||||||
}
|
|
||||||
|
|
||||||
source_path_ =
|
|
||||||
utils::path::combine(buffer_directory, {utils::create_uuid_string()});
|
|
||||||
nf_ = utils::file::file::open_or_create_file(source_path_);
|
|
||||||
if (not*nf_) {
|
|
||||||
throw std::runtime_error(fmt::format("failed to create buffer file|err|{}",
|
|
||||||
utils::get_last_error_code()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not nf_->truncate(ring_size * chunk_size)) {
|
|
||||||
nf_->close();
|
|
||||||
throw std::runtime_error(fmt::format("failed to resize buffer file|err|{}",
|
|
||||||
utils::get_last_error_code()));
|
|
||||||
}
|
|
||||||
|
|
||||||
reader_thread_ =
|
|
||||||
std::make_unique<std::thread>([this]() { background_reader_thread(); });
|
|
||||||
event_system::instance().raise<download_begin>(fsi_.api_path, source_path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ring_buffer_open_file::~ring_buffer_open_file() {
|
ring_buffer_open_file::~ring_buffer_open_file() {
|
||||||
@ -87,59 +52,18 @@ ring_buffer_open_file::~ring_buffer_open_file() {
|
|||||||
|
|
||||||
close();
|
close();
|
||||||
|
|
||||||
|
if (not nf_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nf_->close();
|
nf_->close();
|
||||||
|
nf_.reset();
|
||||||
|
|
||||||
if (not utils::file::file(source_path_).remove()) {
|
if (not utils::file::file(source_path_).remove()) {
|
||||||
utils::error::raise_api_path_error(
|
utils::error::raise_api_path_error(
|
||||||
function_name, fsi_.api_path, source_path_,
|
function_name, get_api_path(), source_path_,
|
||||||
utils::get_last_error_code(), "failed to delete file");
|
utils::get_last_error_code(), "failed to delete file");
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_thread_->join();
|
|
||||||
reader_thread_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ring_buffer_open_file::background_reader_thread() {
|
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
auto next_chunk = ring_pos_;
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
|
|
||||||
while (not stop_requested_) {
|
|
||||||
chunk_lock.lock();
|
|
||||||
|
|
||||||
next_chunk = next_chunk + 1U > ring_end_ ? ring_begin_ : next_chunk + 1U;
|
|
||||||
const auto check_and_wait = [this, &chunk_lock, &next_chunk]() {
|
|
||||||
if (stop_requested_) {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_read_state().all()) {
|
|
||||||
chunk_notify_.wait(chunk_lock);
|
|
||||||
next_chunk = ring_pos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (not ring_state_[next_chunk % ring_state_.size()]) {
|
|
||||||
check_and_wait();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
|
|
||||||
download_chunk(next_chunk, true);
|
|
||||||
|
|
||||||
chunk_lock.lock();
|
|
||||||
check_and_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
event_system::instance().raise<download_end>(fsi_.api_path, source_path_,
|
|
||||||
api_error::download_stopped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ring_buffer_open_file::can_handle_file(std::uint64_t file_size,
|
auto ring_buffer_open_file::can_handle_file(std::uint64_t file_size,
|
||||||
@ -148,272 +72,80 @@ auto ring_buffer_open_file::can_handle_file(std::uint64_t file_size,
|
|||||||
return file_size >= (static_cast<std::uint64_t>(ring_size) * chunk_size);
|
return file_size >= (static_cast<std::uint64_t>(ring_size) * chunk_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ring_buffer_open_file::close() -> bool {
|
|
||||||
stop_requested_ = true;
|
|
||||||
|
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
|
|
||||||
return open_file_base::close();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ring_buffer_open_file::download_chunk(std::size_t chunk, bool skip_active)
|
|
||||||
-> api_error {
|
|
||||||
unique_mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
const auto unlock_and_notify = [this, &chunk_lock]() {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
chunk_lock.unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto unlock_and_return =
|
|
||||||
[&unlock_and_notify](api_error res) -> api_error {
|
|
||||||
unlock_and_notify();
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (chunk < ring_begin_ || chunk > ring_end_) {
|
|
||||||
return unlock_and_return(api_error::invalid_ring_buffer_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active_downloads_.find(chunk) != active_downloads_.end()) {
|
|
||||||
if (skip_active) {
|
|
||||||
return unlock_and_return(api_error::success);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto active_download = active_downloads_.at(chunk);
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
return active_download->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not ring_state_[chunk % ring_state_.size()]) {
|
|
||||||
return unlock_and_return(api_error::success);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto active_download{std::make_shared<download>()};
|
|
||||||
active_downloads_[chunk] = active_download;
|
|
||||||
ring_state_[chunk % ring_state_.size()] = false;
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
data_buffer buffer;
|
|
||||||
auto data_offset{chunk * chunk_size_};
|
|
||||||
auto data_size{
|
|
||||||
chunk == (total_chunks_ - 1U) ? last_chunk_size_ : chunk_size_,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto res{
|
|
||||||
provider_.read_file_bytes(fsi_.api_path, data_size, data_offset, buffer,
|
|
||||||
stop_requested_),
|
|
||||||
};
|
|
||||||
|
|
||||||
chunk_lock.lock();
|
|
||||||
if (res == api_error::success) {
|
|
||||||
auto progress =
|
|
||||||
(static_cast<double>(chunk + 1U) / static_cast<double>(total_chunks_)) *
|
|
||||||
100.0;
|
|
||||||
event_system::instance().raise<download_progress>(fsi_.api_path,
|
|
||||||
source_path_, progress);
|
|
||||||
res = (chunk >= ring_begin_ && chunk <= ring_end_)
|
|
||||||
? do_io([&]() -> api_error {
|
|
||||||
std::size_t bytes_written{};
|
|
||||||
if (nf_->write(buffer,
|
|
||||||
(chunk % ring_state_.size()) * chunk_size_,
|
|
||||||
&bytes_written)) {
|
|
||||||
return api_error::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return api_error::os_error;
|
|
||||||
})
|
|
||||||
: api_error::invalid_ring_buffer_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
active_downloads_.erase(chunk);
|
|
||||||
unlock_and_notify();
|
|
||||||
|
|
||||||
active_download->notify(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ring_buffer_open_file::forward(std::size_t count) {
|
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
if ((ring_pos_ + count) > (total_chunks_ - 1U)) {
|
|
||||||
count = (total_chunks_ - 1U) - ring_pos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ring_pos_ + count) <= ring_end_) {
|
|
||||||
ring_pos_ += count;
|
|
||||||
} else {
|
|
||||||
auto added = count - (ring_end_ - ring_pos_);
|
|
||||||
if (added >= ring_state_.size()) {
|
|
||||||
ring_state_.set(0U, ring_state_.size(), true);
|
|
||||||
ring_pos_ += count;
|
|
||||||
ring_begin_ += added;
|
|
||||||
} else {
|
|
||||||
for (std::size_t idx = 0U; idx < added; ++idx) {
|
|
||||||
ring_state_[(ring_begin_ + idx) % ring_state_.size()] = true;
|
|
||||||
}
|
|
||||||
ring_begin_ += added;
|
|
||||||
ring_pos_ += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ring_buffer_open_file::get_read_state() const -> boost::dynamic_bitset<> {
|
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
|
||||||
auto read_state = ring_state_;
|
|
||||||
return read_state.flip();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ring_buffer_open_file::get_read_state(std::size_t chunk) const -> bool {
|
|
||||||
recur_mutex_lock file_lock(file_mtx_);
|
|
||||||
return not ring_state_[chunk % ring_state_.size()];
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ring_buffer_open_file::native_operation(
|
auto ring_buffer_open_file::native_operation(
|
||||||
i_open_file::native_operation_callback callback) -> api_error {
|
i_open_file::native_operation_callback callback) -> api_error {
|
||||||
return do_io([&]() -> api_error { return callback(nf_->get_handle()); });
|
return do_io([&]() -> api_error { return callback(nf_->get_handle()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ring_buffer_open_file::reverse(std::size_t count) {
|
auto ring_buffer_open_file::on_check_start() -> bool {
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
count = std::min(ring_pos_, count);
|
|
||||||
|
|
||||||
if ((ring_pos_ - count) >= ring_begin_) {
|
if (nf_) {
|
||||||
ring_pos_ -= count;
|
return true;
|
||||||
} else {
|
|
||||||
auto removed = count - (ring_pos_ - ring_begin_);
|
|
||||||
if (removed >= ring_state_.size()) {
|
|
||||||
ring_state_.set(0U, ring_state_.size(), true);
|
|
||||||
ring_pos_ -= count;
|
|
||||||
ring_begin_ = ring_pos_;
|
|
||||||
} else {
|
|
||||||
for (std::size_t idx = 0U; idx < removed; ++idx) {
|
|
||||||
ring_state_[(ring_end_ - idx) % ring_state_.size()] = true;
|
|
||||||
}
|
|
||||||
ring_begin_ -= removed;
|
|
||||||
ring_pos_ -= count;
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
auto buffer_directory{utils::path::get_parent_path(source_path_)};
|
||||||
}
|
if (not utils::file::directory(buffer_directory).create_directory()) {
|
||||||
|
|
||||||
auto ring_buffer_open_file::read(std::size_t read_size,
|
|
||||||
std::uint64_t read_offset, data_buffer &data)
|
|
||||||
-> api_error {
|
|
||||||
if (fsi_.directory) {
|
|
||||||
return api_error::invalid_operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
read_size = utils::calculate_read_size(fsi_.size, read_size, read_offset);
|
|
||||||
if (read_size == 0U) {
|
|
||||||
return api_error::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto begin_chunk{static_cast<std::size_t>(read_offset / chunk_size_)};
|
|
||||||
read_offset = read_offset - (begin_chunk * chunk_size_);
|
|
||||||
|
|
||||||
auto res{api_error::success};
|
|
||||||
|
|
||||||
unique_mutex_lock read_lock(read_mtx_);
|
|
||||||
for (std::size_t chunk = begin_chunk;
|
|
||||||
not stop_requested_ && (res == api_error::success) && (read_size > 0U);
|
|
||||||
++chunk) {
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
if (chunk > ring_pos_) {
|
|
||||||
forward(chunk - ring_pos_);
|
|
||||||
} else if (chunk < ring_pos_) {
|
|
||||||
reverse(ring_pos_ - chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = download_chunk(chunk, false);
|
|
||||||
if (res != api_error::success) {
|
|
||||||
if (not stop_requested_ &&
|
|
||||||
res == api_error::invalid_ring_buffer_position) {
|
|
||||||
read_lock.unlock();
|
|
||||||
|
|
||||||
// TODO limit retry
|
|
||||||
return read(read_size, read_offset, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
auto to_read{
|
|
||||||
std::min(static_cast<std::size_t>(chunk_size_ - read_offset),
|
|
||||||
read_size),
|
|
||||||
};
|
|
||||||
|
|
||||||
res = do_io([&]() -> api_error {
|
|
||||||
data_buffer buffer(to_read);
|
|
||||||
|
|
||||||
std::size_t bytes_read{};
|
|
||||||
auto result = nf_->read(buffer,
|
|
||||||
(((chunk % ring_state_.size()) * chunk_size_) +
|
|
||||||
read_offset),
|
|
||||||
&bytes_read)
|
|
||||||
? api_error::success
|
|
||||||
: api_error::os_error;
|
|
||||||
|
|
||||||
if (result != api_error::success) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_timeout();
|
|
||||||
|
|
||||||
data.insert(data.end(), buffer.begin(), buffer.end());
|
|
||||||
read_size -= bytes_read;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
read_offset = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stop_requested_ ? api_error::download_stopped : res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ring_buffer_open_file::set(std::size_t first_chunk,
|
|
||||||
std::size_t current_chunk) {
|
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
|
||||||
if (first_chunk >= total_chunks_) {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
throw std::runtime_error("first chunk must be less than total chunks");
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_begin_ = first_chunk;
|
|
||||||
ring_end_ =
|
|
||||||
std::min(total_chunks_ - 1U, ring_begin_ + ring_state_.size() - 1U);
|
|
||||||
|
|
||||||
if (current_chunk > ring_end_) {
|
|
||||||
chunk_notify_.notify_all();
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"current chunk must be less than or equal to last chunk");
|
fmt::format("failed to create buffer directory|path|{}|err|{}",
|
||||||
|
buffer_directory, utils::get_last_error_code()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ring_pos_ = current_chunk;
|
nf_ = utils::file::file::open_or_create_file(source_path_);
|
||||||
ring_state_.set(0U, ring_state_.size(), false);
|
if (not nf_ || not *nf_) {
|
||||||
|
throw std::runtime_error(fmt::format("failed to create buffer file|err|{}",
|
||||||
|
utils::get_last_error_code()));
|
||||||
|
}
|
||||||
|
|
||||||
chunk_notify_.notify_all();
|
if (not nf_->truncate(get_ring_size() * get_chunk_size())) {
|
||||||
|
nf_->close();
|
||||||
|
nf_.reset();
|
||||||
|
|
||||||
|
throw std::runtime_error(fmt::format("failed to resize buffer file|err|{}",
|
||||||
|
utils::get_last_error_code()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ring_buffer_open_file::set_api_path(const std::string &api_path) {
|
auto ring_buffer_open_file::on_chunk_downloaded(
|
||||||
mutex_lock chunk_lock(chunk_mtx_);
|
std::size_t chunk, const data_buffer &buffer) -> api_error {
|
||||||
open_file_base::set_api_path(api_path);
|
return do_io([&]() -> api_error {
|
||||||
chunk_notify_.notify_all();
|
std::size_t bytes_written{};
|
||||||
|
if (nf_->write(buffer, (chunk % get_ring_size()) * get_chunk_size(),
|
||||||
|
&bytes_written)) {
|
||||||
|
return api_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return api_error::os_error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_open_file::on_read_chunk(
|
||||||
|
std::size_t chunk, std::size_t read_size, std::uint64_t read_offset,
|
||||||
|
data_buffer &data, std::size_t &bytes_read) -> api_error {
|
||||||
|
data_buffer buffer(read_size);
|
||||||
|
auto res = do_io([&]() -> api_error {
|
||||||
|
return nf_->read(
|
||||||
|
buffer,
|
||||||
|
(((chunk % get_ring_size()) * get_chunk_size()) + read_offset),
|
||||||
|
&bytes_read)
|
||||||
|
? api_error::success
|
||||||
|
: api_error::os_error;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res != api_error::success) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.insert(data.end(), buffer.begin(), buffer.end());
|
||||||
|
return api_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ring_buffer_open_file::use_buffer(
|
||||||
|
std::size_t /* chunk */,
|
||||||
|
std::function<api_error(data_buffer &)> func) -> api_error {
|
||||||
|
data_buffer buffer;
|
||||||
|
return func(buffer);
|
||||||
}
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
@ -51,8 +51,6 @@ void full_server::handle_get_directory_items(const httplib::Request &req,
|
|||||||
|
|
||||||
void full_server::handle_get_drive_information(const httplib::Request & /*req*/,
|
void full_server::handle_get_drive_information(const httplib::Request & /*req*/,
|
||||||
httplib::Response &res) {
|
httplib::Response &res) {
|
||||||
auto dir_size =
|
|
||||||
utils::file::directory(get_config().get_cache_directory()).size();
|
|
||||||
res.set_content(
|
res.set_content(
|
||||||
json({
|
json({
|
||||||
{"cache_space_used", cache_size_mgr::instance().size()},
|
{"cache_space_used", cache_size_mgr::instance().size()},
|
||||||
|
@ -52,6 +52,8 @@ auto from_api_error(const api_error &err) -> int {
|
|||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
case api_error::file_in_use:
|
case api_error::file_in_use:
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
case api_error::invalid_handle:
|
||||||
|
return -EBADF;
|
||||||
case api_error::invalid_operation:
|
case api_error::invalid_operation:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
case api_error::item_not_found:
|
case api_error::item_not_found:
|
||||||
|
@ -52,11 +52,14 @@ TEST_F(direct_open_file_test, read_full_file) {
|
|||||||
fsi.directory = false;
|
fsi.directory = false;
|
||||||
fsi.size = test_chunk_size * 32U;
|
fsi.size = test_chunk_size * 32U;
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(provider, read_file_bytes)
|
EXPECT_CALL(provider, read_file_bytes)
|
||||||
.WillRepeatedly([&source_file](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &source_file](
|
||||||
std::size_t size, std::uint64_t offset,
|
const std::string & /* api_path */, std::size_t size,
|
||||||
data_buffer &data,
|
std::uint64_t offset, data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -111,11 +114,14 @@ TEST_F(direct_open_file_test, read_full_file_in_reverse) {
|
|||||||
fsi.directory = false;
|
fsi.directory = false;
|
||||||
fsi.size = test_chunk_size * 32U;
|
fsi.size = test_chunk_size * 32U;
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(provider, read_file_bytes)
|
EXPECT_CALL(provider, read_file_bytes)
|
||||||
.WillRepeatedly([&source_file](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &source_file](
|
||||||
std::size_t size, std::uint64_t offset,
|
const std::string & /* api_path */, std::size_t size,
|
||||||
data_buffer &data,
|
std::uint64_t offset, data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -170,11 +176,14 @@ TEST_F(direct_open_file_test, read_full_file_in_partial_chunks) {
|
|||||||
fsi.api_path = "/test.txt";
|
fsi.api_path = "/test.txt";
|
||||||
fsi.size = test_chunk_size * 32U;
|
fsi.size = test_chunk_size * 32U;
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(provider, read_file_bytes)
|
EXPECT_CALL(provider, read_file_bytes)
|
||||||
.WillRepeatedly([&source_file](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &source_file](
|
||||||
std::size_t size, std::uint64_t offset,
|
const std::string & /* api_path */, std::size_t size,
|
||||||
data_buffer &data,
|
std::uint64_t offset, data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -226,11 +235,14 @@ TEST_F(direct_open_file_test, read_full_file_in_partial_chunks_in_reverse) {
|
|||||||
fsi.api_path = "/test.txt";
|
fsi.api_path = "/test.txt";
|
||||||
fsi.size = test_chunk_size * 32U;
|
fsi.size = test_chunk_size * 32U;
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(provider, read_file_bytes)
|
EXPECT_CALL(provider, read_file_bytes)
|
||||||
.WillRepeatedly([&source_file](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &source_file](
|
||||||
std::size_t size, std::uint64_t offset,
|
const std::string & /* api_path */, std::size_t size,
|
||||||
data_buffer &data,
|
std::uint64_t offset, data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
|
@ -748,6 +748,17 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_pinned) {
|
|||||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||||
file_manager mgr(*cfg, mp);
|
file_manager mgr(*cfg, mp);
|
||||||
|
|
||||||
|
EXPECT_CALL(mp, get_filesystem_item)
|
||||||
|
.WillRepeatedly([](const std::string &api_path, bool directory,
|
||||||
|
filesystem_item &fsi) -> api_error {
|
||||||
|
fsi.api_path = api_path;
|
||||||
|
fsi.api_parent = utils::path::get_parent_api_path(api_path);
|
||||||
|
fsi.directory = directory;
|
||||||
|
fsi.size = 2U;
|
||||||
|
fsi.source_path = "/test/test_open.src";
|
||||||
|
return api_error::success;
|
||||||
|
});
|
||||||
|
|
||||||
EXPECT_CALL(mp, get_item_meta(_, META_PINNED, _))
|
EXPECT_CALL(mp, get_item_meta(_, META_PINNED, _))
|
||||||
.WillOnce([](const std::string &api_path, const std::string &key,
|
.WillOnce([](const std::string &api_path, const std::string &key,
|
||||||
std::string &value) -> api_error {
|
std::string &value) -> api_error {
|
||||||
@ -811,9 +822,11 @@ TEST_F(file_manager_test, evict_file_fails_if_unable_to_get_filesystem_item) {
|
|||||||
file_manager mgr(*cfg, mp);
|
file_manager mgr(*cfg, mp);
|
||||||
|
|
||||||
EXPECT_CALL(mp, get_filesystem_item)
|
EXPECT_CALL(mp, get_filesystem_item)
|
||||||
.WillRepeatedly(
|
.WillRepeatedly([](const std::string & /* api_path */,
|
||||||
[](const std::string &api_path, bool directory,
|
bool /* directory */,
|
||||||
filesystem_item &fsi) -> api_error { return api_error::error; });
|
filesystem_item & /* fsi */) -> api_error {
|
||||||
|
return api_error::error;
|
||||||
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(mgr.evict_file("/test_open.txt"));
|
EXPECT_FALSE(mgr.evict_file("/test_open.txt"));
|
||||||
}
|
}
|
||||||
@ -943,6 +956,7 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_modified) {
|
|||||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||||
|
|
||||||
file_manager mgr(*cfg, mp);
|
file_manager mgr(*cfg, mp);
|
||||||
|
|
||||||
EXPECT_CALL(mp, get_filesystem_item)
|
EXPECT_CALL(mp, get_filesystem_item)
|
||||||
.WillOnce([](const std::string &api_path, bool directory,
|
.WillOnce([](const std::string &api_path, bool directory,
|
||||||
filesystem_item &fsi) -> api_error {
|
filesystem_item &fsi) -> api_error {
|
||||||
@ -957,11 +971,12 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_modified) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
auto file = std::make_shared<mock_open_file>();
|
auto file = std::make_shared<mock_open_file>();
|
||||||
EXPECT_CALL(*file, is_directory).WillOnce(Return(false));
|
|
||||||
EXPECT_CALL(*file, add).WillOnce(Return());
|
EXPECT_CALL(*file, add).WillOnce(Return());
|
||||||
EXPECT_CALL(*file, get_api_path).WillRepeatedly(Return("/test_evict.txt"));
|
EXPECT_CALL(*file, get_api_path).WillRepeatedly(Return("/test_evict.txt"));
|
||||||
EXPECT_CALL(*file, get_source_path).WillRepeatedly(Return("/test_evict.src"));
|
EXPECT_CALL(*file, get_source_path).WillRepeatedly(Return("/test_evict.src"));
|
||||||
EXPECT_CALL(*file, is_modified).Times(2).WillRepeatedly(Return(true));
|
EXPECT_CALL(*file, is_directory).WillOnce(Return(false));
|
||||||
|
EXPECT_CALL(*file, is_modified).WillRepeatedly(Return(true));
|
||||||
|
EXPECT_CALL(*file, is_write_supported).WillRepeatedly(Return(true));
|
||||||
|
|
||||||
std::uint64_t handle{};
|
std::uint64_t handle{};
|
||||||
std::shared_ptr<i_open_file> open_file;
|
std::shared_ptr<i_open_file> open_file;
|
||||||
@ -984,20 +999,21 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_not_complete) {
|
|||||||
filesystem_item &fsi) -> api_error {
|
filesystem_item &fsi) -> api_error {
|
||||||
EXPECT_STREQ("/test_evict.txt", api_path.c_str());
|
EXPECT_STREQ("/test_evict.txt", api_path.c_str());
|
||||||
EXPECT_FALSE(directory);
|
EXPECT_FALSE(directory);
|
||||||
fsi.api_path = api_path;
|
|
||||||
fsi.api_parent = utils::path::get_parent_api_path(api_path);
|
fsi.api_parent = utils::path::get_parent_api_path(api_path);
|
||||||
|
fsi.api_path = api_path;
|
||||||
fsi.directory = directory;
|
fsi.directory = directory;
|
||||||
fsi.size = 1U;
|
fsi.size = 1U;
|
||||||
return api_error::success;
|
return api_error::success;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto file = std::make_shared<mock_open_file>();
|
auto file = std::make_shared<mock_open_file>();
|
||||||
EXPECT_CALL(*file, is_directory).WillOnce(Return(false));
|
|
||||||
EXPECT_CALL(*file, add).WillOnce(Return());
|
EXPECT_CALL(*file, add).WillOnce(Return());
|
||||||
EXPECT_CALL(*file, get_api_path).WillRepeatedly(Return("/test_evict.txt"));
|
EXPECT_CALL(*file, get_api_path).WillRepeatedly(Return("/test_evict.txt"));
|
||||||
EXPECT_CALL(*file, get_source_path).WillRepeatedly(Return("/test_evict.src"));
|
EXPECT_CALL(*file, get_source_path).WillRepeatedly(Return("/test_evict.src"));
|
||||||
EXPECT_CALL(*file, is_modified).Times(2).WillRepeatedly(Return(false));
|
EXPECT_CALL(*file, is_complete).WillRepeatedly(Return(false));
|
||||||
EXPECT_CALL(*file, is_complete).Times(2).WillRepeatedly(Return(false));
|
EXPECT_CALL(*file, is_directory).WillOnce(Return(false));
|
||||||
|
EXPECT_CALL(*file, is_modified).WillRepeatedly(Return(false));
|
||||||
|
EXPECT_CALL(*file, is_write_supported).WillRepeatedly(Return(true));
|
||||||
EXPECT_CALL(mp, set_item_meta("/test_evict.txt", META_SOURCE, _))
|
EXPECT_CALL(mp, set_item_meta("/test_evict.txt", META_SOURCE, _))
|
||||||
.WillOnce(Return(api_error::success));
|
.WillOnce(Return(api_error::success));
|
||||||
|
|
||||||
|
@ -308,11 +308,14 @@ TEST_F(ring_buffer_open_file_test, read_full_file) {
|
|||||||
fsi.size = test_chunk_size * 33u + 11u;
|
fsi.size = test_chunk_size * 33u + 11u;
|
||||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(mp, read_file_bytes)
|
EXPECT_CALL(mp, read_file_bytes)
|
||||||
.WillRepeatedly([&nf](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||||
std::size_t size, std::uint64_t offset,
|
std::size_t size, std::uint64_t offset,
|
||||||
data_buffer &data,
|
data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -371,11 +374,14 @@ TEST_F(ring_buffer_open_file_test, read_full_file_in_reverse) {
|
|||||||
fsi.size = test_chunk_size * 32u;
|
fsi.size = test_chunk_size * 32u;
|
||||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(mp, read_file_bytes)
|
EXPECT_CALL(mp, read_file_bytes)
|
||||||
.WillRepeatedly([&nf](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||||
std::size_t size, std::uint64_t offset,
|
std::size_t size, std::uint64_t offset,
|
||||||
data_buffer &data,
|
data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -434,11 +440,14 @@ TEST_F(ring_buffer_open_file_test, read_full_file_in_partial_chunks) {
|
|||||||
fsi.size = test_chunk_size * 32u;
|
fsi.size = test_chunk_size * 32u;
|
||||||
fsi.source_path = test::generate_test_file_name("test");
|
fsi.source_path = test::generate_test_file_name("test");
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(mp, read_file_bytes)
|
EXPECT_CALL(mp, read_file_bytes)
|
||||||
.WillRepeatedly([&nf](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||||
std::size_t size, std::uint64_t offset,
|
std::size_t size, std::uint64_t offset,
|
||||||
data_buffer &data,
|
data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
@ -499,11 +508,14 @@ TEST_F(ring_buffer_open_file_test,
|
|||||||
fsi.size = test_chunk_size * 32u;
|
fsi.size = test_chunk_size * 32u;
|
||||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||||
|
|
||||||
|
std::mutex read_mtx;
|
||||||
EXPECT_CALL(mp, read_file_bytes)
|
EXPECT_CALL(mp, read_file_bytes)
|
||||||
.WillRepeatedly([&nf](const std::string & /* api_path */,
|
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||||
std::size_t size, std::uint64_t offset,
|
std::size_t size, std::uint64_t offset,
|
||||||
data_buffer &data,
|
data_buffer &data,
|
||||||
stop_type &stop_requested) -> api_error {
|
stop_type &stop_requested) -> api_error {
|
||||||
|
mutex_lock lock(read_mtx);
|
||||||
|
|
||||||
EXPECT_FALSE(stop_requested);
|
EXPECT_FALSE(stop_requested);
|
||||||
std::size_t bytes_read{};
|
std::size_t bytes_read{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
|
Reference in New Issue
Block a user