From 05a427f3ed2ae1d8c129dc450b9042e81e30b13b Mon Sep 17 00:00:00 2001 From: Denis Demidov Date: Thu, 14 Nov 2019 12:02:20 +0300 Subject: [PATCH 1/3] Replace fstream with cstdio --- ev3dev.cpp | 268 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 167 insertions(+), 101 deletions(-) diff --git a/ev3dev.cpp b/ev3dev.cpp index 6237e48..8aa249e 100644 --- a/ev3dev.cpp +++ b/ev3dev.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -38,7 +37,8 @@ #include #include #include -#include +#include +#include #include #include @@ -66,6 +66,154 @@ static const int bits_per_long = sizeof(long) * 8; namespace ev3dev { namespace { +//----------------------------------------------------------------------------- +class file_reader { + public: + file_reader() : f(0) {} + + file_reader(file_reader &&other) + : fname(std::move(other.fname)), f(other.f) + { + other.f = 0; + } + + ~file_reader() { + if (f) fclose(f); + } + + bool is_open() const { + return f != 0; + } + + void open(const std::string &_fname) { + fname = _fname; + f = fopen(fname.c_str(), "r"); + + if (!f) { + throw std::system_error(std::make_error_code(static_cast(errno)), + std::string("Failed to open \"") + fname + "\""); + } + } + + std::string get_string() { + char buf[256]; + try_read([this, &buf](){ return 1 == fscanf(f, "%255s", buf); }); + return buf; + } + + std::string get_line() { + char buf[256]; + try_read([this, &buf](){ + if (!fgets(buf, 255, f)) return false; + char *pos; + if ((pos=strchr(buf, '\n')) != NULL) *pos = '\0'; + return true; + }); + return buf; + } + + int get_int() { + int v; + try_read([this, &v](){return 1 == fscanf(f, "%d", &v); }); + return v; + } + + void get_data(char *data, size_t count) { + try_read([this, &data, count](){ return count == fread(data, count, count, f); }); + } + private: + std::string fname; + FILE *f; + + void reopen() { + if (f) fclose(f); + f = fopen(fname.c_str(), "r"); + } + + template + void try_read(Callable w) { + for(int attempt = 0; attempt < 2; ++attempt) { + fseek(f, 0, SEEK_SET); + + if (w()) return; + + // Failed to read the value. + // This could mean the sysfs attribute was recreated and the + // corresponding file handle got stale. Lets close the file and try + // again (once): + if (attempt != 0) { + throw std::system_error(std::make_error_code(static_cast(errno)), fname); + } + + reopen(); + } + } +}; + +//----------------------------------------------------------------------------- +class file_writer { + public: + file_writer() : f(0) {} + + file_writer(file_writer &&other) + : fname(std::move(other.fname)), f(other.f) + { + other.f = 0; + } + + ~file_writer() { + if (f) fclose(f); + } + + bool is_open() const { + return f != 0; + } + + void open(const std::string &_fname) { + fname = _fname; + f = fopen(fname.c_str(), "w"); + + if (!f) { + throw std::system_error(std::make_error_code(static_cast(errno)), + std::string("Failed to open \"") + fname + "\""); + } + } + + void put_string(const std::string &v) { + try_write([this, &v](){ return EOF != fputs(v.c_str(), f); }); + } + + void put_int(int v) { + try_write([this, v](){ return fprintf(f, "%d", v) >= 0; }); + } + + private: + std::string fname; + FILE *f; + + void reopen() { + if (f) fclose(f); + f = fopen(fname.c_str(), "w"); + } + + template + void try_write(Callable w) { + for(int attempt = 0; attempt < 2; ++attempt) { + if (w()) return; + + // Failed to write the value. + // This could mean the sysfs attribute was recreated and the + // corresponding file handle got stale. Lets close the file and try + // again (once): + if (attempt != 0) { + throw std::system_error(std::make_error_code(static_cast(errno)), fname); + } + + reopen(); + } + } +}; + // This class implements a small LRU cache. It assumes the number of elements // is small, and so uses a simple linear search. template @@ -118,46 +266,24 @@ class lru_cache { }; // A global cache of files. -std::ifstream& ifstream_cache(const std::string &path) { - static lru_cache cache(FSTREAM_CACHE_SIZE); +file_reader& reader_cache(const std::string &path) { + static lru_cache cache(FSTREAM_CACHE_SIZE); static std::mutex mx; std::lock_guard lock(mx); - return cache[path]; + file_reader &f = cache[path]; + if (!f.is_open()) f.open(path); + return f; } -std::ofstream& ofstream_cache(const std::string &path) { - static lru_cache cache(FSTREAM_CACHE_SIZE); +file_writer& writer_cache(const std::string &path) { + static lru_cache cache(FSTREAM_CACHE_SIZE); static std::mutex mx; std::lock_guard lock(mx); - return cache[path]; -} - -//----------------------------------------------------------------------------- -std::ofstream &ofstream_open(const std::string &path) { - std::ofstream &file = ofstream_cache(path); - if (!file.is_open()) { - // Don't buffer writes to avoid latency. Also saves a bit of memory. - file.rdbuf()->pubsetbuf(NULL, 0); - file.open(path); - } else { - // Clear the error bits in case something happened. - file.clear(); - } - return file; -} - -std::ifstream &ifstream_open(const std::string &path) { - std::ifstream &file = ifstream_cache(path); - if (!file.is_open()) { - file.open(path); - } else { - // Clear the flags bits in case something happened (like reaching EOF). - file.clear(); - file.seekg(0, std::ios::beg); - } - return file; + file_writer &f = cache[path]; + if (!f.is_open()) f.open(path); + return f; } } // namespace @@ -243,25 +369,7 @@ int device::get_attr_int(const std::string &name) const { if (_path.empty()) throw system_error(make_error_code(errc::function_not_supported), "no device connected"); - for(int attempt = 0; attempt < 2; ++attempt) { - ifstream &is = ifstream_open(_path + name); - if (is.is_open()) { - int result = 0; - try { - is >> result; - return result; - } catch(...) { - // This could mean the sysfs attribute was recreated and the - // corresponding file handle got stale. Lets close the file and try - // again (once): - if (attempt != 0) throw; - - is.close(); - is.clear(); - } - } else break; - } - throw system_error(make_error_code(errc::no_such_device), _path+name); + return reader_cache(_path + name).get_int(); } //----------------------------------------------------------------------------- @@ -271,23 +379,7 @@ void device::set_attr_int(const std::string &name, int value) { if (_path.empty()) throw system_error(make_error_code(errc::function_not_supported), "no device connected"); - for(int attempt = 0; attempt < 2; ++attempt) { - ofstream &os = ofstream_open(_path + name); - if (os.is_open()) { - if (os << value) return; - - // An error could mean that sysfs attribute was recreated and the cached - // file handle is stale. Lets close the file and try again (once): - if (attempt == 0 && errno == ENODEV) { - os.close(); - os.clear(); - } else { - throw system_error(std::error_code(errno, std::system_category())); - } - } else { - throw system_error(make_error_code(errc::no_such_device), _path + name); - } - } + writer_cache(_path + name).put_int(value); } //----------------------------------------------------------------------------- @@ -297,14 +389,7 @@ std::string device::get_attr_string(const std::string &name) const { if (_path.empty()) throw system_error(make_error_code(errc::function_not_supported), "no device connected"); - ifstream &is = ifstream_open(_path + name); - if (is.is_open()) { - string result; - is >> result; - return result; - } - - throw system_error(make_error_code(errc::no_such_device), _path+name); + return reader_cache(_path + name).get_string(); } //----------------------------------------------------------------------------- @@ -314,13 +399,7 @@ void device::set_attr_string(const std::string &name, const std::string &value) if (_path.empty()) throw system_error(make_error_code(errc::function_not_supported), "no device connected"); - ofstream &os = ofstream_open(_path + name); - if (os.is_open()) { - if (!(os << value)) throw system_error(std::error_code(errno, std::system_category())); - return; - } - - throw system_error(make_error_code(errc::no_such_device), _path+name); + writer_cache(_path + name).put_string(value); } //----------------------------------------------------------------------------- @@ -330,14 +409,7 @@ std::string device::get_attr_line(const std::string &name) const { if (_path.empty()) throw system_error(make_error_code(errc::function_not_supported), "no device connected"); - ifstream &is = ifstream_open(_path + name); - if (is.is_open()) { - string result; - getline(is, result); - return result; - } - - throw system_error(make_error_code(errc::no_such_device), _path+name); + return reader_cache(_path + name).get_line(); } //----------------------------------------------------------------------------- @@ -513,14 +585,8 @@ const std::vector& sensor::bin_data() const { _bin_data.resize(num_values() * value_size); } - const string fname = _path + "bin_data"; - ifstream &is = ifstream_open(fname); - if (is.is_open()) { - is.read(_bin_data.data(), _bin_data.size()); - return _bin_data; - } - - throw system_error(make_error_code(errc::no_such_device), fname); + reader_cache(_path + "bin_data").get_data(_bin_data.data(), _bin_data.size()); + return _bin_data; } //----------------------------------------------------------------------------- From d4dae3e9edd705c77716748114294e5370ccf748 Mon Sep 17 00:00:00 2001 From: Denis Demidov Date: Sun, 24 Nov 2019 09:38:03 +0300 Subject: [PATCH 2/3] fseek(0) in writer --- ev3dev.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ev3dev.cpp b/ev3dev.cpp index 8aa249e..d1ebaa8 100644 --- a/ev3dev.cpp +++ b/ev3dev.cpp @@ -199,6 +199,8 @@ class file_writer { template void try_write(Callable w) { for(int attempt = 0; attempt < 2; ++attempt) { + fseek(f, 0, SEEK_SET); + if (w()) return; // Failed to write the value. From bb8a87314cb366615fd3dab929502f853076c175 Mon Sep 17 00:00:00 2001 From: Denis Demidov Date: Sun, 24 Nov 2019 13:25:51 +0300 Subject: [PATCH 3/3] Disable buffering --- ev3dev.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ev3dev.cpp b/ev3dev.cpp index d1ebaa8..8f97efe 100644 --- a/ev3dev.cpp +++ b/ev3dev.cpp @@ -88,6 +88,7 @@ class file_reader { void open(const std::string &_fname) { fname = _fname; f = fopen(fname.c_str(), "r"); + setbuf(f, NULL); if (!f) { throw std::system_error(std::make_error_code(static_cast(errno)), @@ -128,6 +129,7 @@ class file_reader { void reopen() { if (f) fclose(f); f = fopen(fname.c_str(), "r"); + setbuf(f, NULL); } template @@ -172,6 +174,7 @@ class file_writer { void open(const std::string &_fname) { fname = _fname; f = fopen(fname.c_str(), "w"); + setbuf(f, NULL); if (!f) { throw std::system_error(std::make_error_code(static_cast(errno)), @@ -194,6 +197,7 @@ class file_writer { void reopen() { if (f) fclose(f); f = fopen(fname.c_str(), "w"); + setbuf(f, NULL); } template