27
27
28
28
#include < iostream>
29
29
#include < sstream>
30
- #include < fstream>
31
30
#include < list>
32
31
#include < map>
33
32
#include < array>
38
37
#include < thread>
39
38
#include < stdexcept>
40
39
#include < string.h>
41
- #include < math.h>
40
+ #include < cmath>
41
+ #include < cstdio>
42
42
43
43
#include < dirent.h>
44
44
#include < sys/mman.h>
@@ -66,6 +66,154 @@ static const int bits_per_long = sizeof(long) * 8;
66
66
namespace ev3dev {
67
67
namespace {
68
68
69
+ // -----------------------------------------------------------------------------
70
+ class file_reader {
71
+ public:
72
+ file_reader () : f(0 ) {}
73
+
74
+ file_reader (file_reader &&other)
75
+ : fname(std::move(other.fname)), f(other.f)
76
+ {
77
+ other.f = 0 ;
78
+ }
79
+
80
+ ~file_reader () {
81
+ if (f) fclose (f);
82
+ }
83
+
84
+ bool is_open () const {
85
+ return f != 0 ;
86
+ }
87
+
88
+ void open (const std::string &_fname) {
89
+ fname = _fname;
90
+ f = fopen (fname.c_str (), " r" );
91
+
92
+ if (!f) {
93
+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)),
94
+ std::string (" Failed to open \" " ) + fname + " \" " );
95
+ }
96
+ }
97
+
98
+ std::string get_string () {
99
+ char buf[256 ];
100
+ try_read ([this , &buf](){ return 1 == fscanf (f, " %255s" , buf); });
101
+ return buf;
102
+ }
103
+
104
+ std::string get_line () {
105
+ char buf[256 ];
106
+ try_read ([this , &buf](){
107
+ if (!fgets (buf, 255 , f)) return false ;
108
+ char *pos;
109
+ if ((pos=strchr (buf, ' \n ' )) != NULL ) *pos = ' \0 ' ;
110
+ return true ;
111
+ });
112
+ return buf;
113
+ }
114
+
115
+ int get_int () {
116
+ int v;
117
+ try_read ([this , &v](){return 1 == fscanf (f, " %d" , &v); });
118
+ return v;
119
+ }
120
+
121
+ void get_data (char *data, size_t count) {
122
+ try_read ([this , &data, count](){ return count == fread (data, count, count, f); });
123
+ }
124
+ private:
125
+ std::string fname;
126
+ FILE *f;
127
+
128
+ void reopen () {
129
+ if (f) fclose (f);
130
+ f = fopen (fname.c_str (), " r" );
131
+ }
132
+
133
+ template <class Callable >
134
+ void try_read (Callable w) {
135
+ for (int attempt = 0 ; attempt < 2 ; ++attempt) {
136
+ fseek (f, 0 , SEEK_SET);
137
+
138
+ if (w ()) return ;
139
+
140
+ // Failed to read the value.
141
+ // This could mean the sysfs attribute was recreated and the
142
+ // corresponding file handle got stale. Lets close the file and try
143
+ // again (once):
144
+ if (attempt != 0 ) {
145
+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)), fname);
146
+ }
147
+
148
+ reopen ();
149
+ }
150
+ }
151
+ };
152
+
153
+ // -----------------------------------------------------------------------------
154
+ class file_writer {
155
+ public:
156
+ file_writer () : f(0 ) {}
157
+
158
+ file_writer (file_writer &&other)
159
+ : fname(std::move(other.fname)), f(other.f)
160
+ {
161
+ other.f = 0 ;
162
+ }
163
+
164
+ ~file_writer () {
165
+ if (f) fclose (f);
166
+ }
167
+
168
+ bool is_open () const {
169
+ return f != 0 ;
170
+ }
171
+
172
+ void open (const std::string &_fname) {
173
+ fname = _fname;
174
+ f = fopen (fname.c_str (), " w" );
175
+
176
+ if (!f) {
177
+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)),
178
+ std::string (" Failed to open \" " ) + fname + " \" " );
179
+ }
180
+ }
181
+
182
+ void put_string (const std::string &v) {
183
+ try_write ([this , &v](){ return EOF != fputs (v.c_str (), f); });
184
+ }
185
+
186
+ void put_int (int v) {
187
+ try_write ([this , v](){ return fprintf (f, " %d" , v) >= 0 ; });
188
+ }
189
+
190
+ private:
191
+ std::string fname;
192
+ FILE *f;
193
+
194
+ void reopen () {
195
+ if (f) fclose (f);
196
+ f = fopen (fname.c_str (), " w" );
197
+ }
198
+
199
+ template <class Callable >
200
+ void try_write (Callable w) {
201
+ for (int attempt = 0 ; attempt < 2 ; ++attempt) {
202
+ if (w ()) return ;
203
+
204
+ // Failed to write the value.
205
+ // This could mean the sysfs attribute was recreated and the
206
+ // corresponding file handle got stale. Lets close the file and try
207
+ // again (once):
208
+ if (attempt != 0 ) {
209
+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)), fname);
210
+ }
211
+
212
+ reopen ();
213
+ }
214
+ }
215
+ };
216
+
69
217
// This class implements a small LRU cache. It assumes the number of elements
70
218
// is small, and so uses a simple linear search.
71
219
template <typename K, typename V>
@@ -118,46 +266,24 @@ class lru_cache {
118
266
};
119
267
120
268
// A global cache of files.
121
- std::ifstream& ifstream_cache (const std::string &path) {
122
- static lru_cache<std::string, std::ifstream > cache (FSTREAM_CACHE_SIZE);
269
+ file_reader& reader_cache (const std::string &path) {
270
+ static lru_cache<std::string, file_reader > cache (FSTREAM_CACHE_SIZE);
123
271
static std::mutex mx;
124
272
125
273
std::lock_guard<std::mutex> lock (mx);
126
- return cache[path];
274
+ file_reader &f = cache[path];
275
+ if (!f.is_open ()) f.open (path);
276
+ return f;
127
277
}
128
278
129
- std::ofstream& ofstream_cache (const std::string &path) {
130
- static lru_cache<std::string, std::ofstream > cache (FSTREAM_CACHE_SIZE);
279
+ file_writer& writer_cache (const std::string &path) {
280
+ static lru_cache<std::string, file_writer > cache (FSTREAM_CACHE_SIZE);
131
281
static std::mutex mx;
132
282
133
283
std::lock_guard<std::mutex> lock (mx);
134
- return cache[path];
135
- }
136
-
137
- // -----------------------------------------------------------------------------
138
- std::ofstream &ofstream_open (const std::string &path) {
139
- std::ofstream &file = ofstream_cache (path);
140
- if (!file.is_open ()) {
141
- // Don't buffer writes to avoid latency. Also saves a bit of memory.
142
- file.rdbuf ()->pubsetbuf (NULL , 0 );
143
- file.open (path);
144
- } else {
145
- // Clear the error bits in case something happened.
146
- file.clear ();
147
- }
148
- return file;
149
- }
150
-
151
- std::ifstream &ifstream_open (const std::string &path) {
152
- std::ifstream &file = ifstream_cache (path);
153
- if (!file.is_open ()) {
154
- file.open (path);
155
- } else {
156
- // Clear the flags bits in case something happened (like reaching EOF).
157
- file.clear ();
158
- file.seekg (0 , std::ios::beg);
159
- }
160
- return file;
284
+ file_writer &f = cache[path];
285
+ if (!f.is_open ()) f.open (path);
286
+ return f;
161
287
}
162
288
163
289
} // namespace
@@ -243,25 +369,7 @@ int device::get_attr_int(const std::string &name) const {
243
369
if (_path.empty ())
244
370
throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
245
371
246
- for (int attempt = 0 ; attempt < 2 ; ++attempt) {
247
- ifstream &is = ifstream_open (_path + name);
248
- if (is.is_open ()) {
249
- int result = 0 ;
250
- try {
251
- is >> result;
252
- return result;
253
- } catch (...) {
254
- // This could mean the sysfs attribute was recreated and the
255
- // corresponding file handle got stale. Lets close the file and try
256
- // again (once):
257
- if (attempt != 0 ) throw ;
258
-
259
- is.close ();
260
- is.clear ();
261
- }
262
- } else break ;
263
- }
264
- throw system_error (make_error_code (errc::no_such_device), _path+name);
372
+ return reader_cache (_path + name).get_int ();
265
373
}
266
374
267
375
// -----------------------------------------------------------------------------
@@ -271,23 +379,7 @@ void device::set_attr_int(const std::string &name, int value) {
271
379
if (_path.empty ())
272
380
throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
273
381
274
- for (int attempt = 0 ; attempt < 2 ; ++attempt) {
275
- ofstream &os = ofstream_open (_path + name);
276
- if (os.is_open ()) {
277
- if (os << value) return ;
278
-
279
- // An error could mean that sysfs attribute was recreated and the cached
280
- // file handle is stale. Lets close the file and try again (once):
281
- if (attempt == 0 && errno == ENODEV) {
282
- os.close ();
283
- os.clear ();
284
- } else {
285
- throw system_error (std::error_code (errno, std::system_category ()));
286
- }
287
- } else {
288
- throw system_error (make_error_code (errc::no_such_device), _path + name);
289
- }
290
- }
382
+ writer_cache (_path + name).put_int (value);
291
383
}
292
384
293
385
// -----------------------------------------------------------------------------
@@ -297,14 +389,7 @@ std::string device::get_attr_string(const std::string &name) const {
297
389
if (_path.empty ())
298
390
throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
299
391
300
- ifstream &is = ifstream_open (_path + name);
301
- if (is.is_open ()) {
302
- string result;
303
- is >> result;
304
- return result;
305
- }
306
-
307
- throw system_error (make_error_code (errc::no_such_device), _path+name);
392
+ return reader_cache (_path + name).get_string ();
308
393
}
309
394
310
395
// -----------------------------------------------------------------------------
@@ -314,13 +399,7 @@ void device::set_attr_string(const std::string &name, const std::string &value)
314
399
if (_path.empty ())
315
400
throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
316
401
317
- ofstream &os = ofstream_open (_path + name);
318
- if (os.is_open ()) {
319
- if (!(os << value)) throw system_error (std::error_code (errno, std::system_category ()));
320
- return ;
321
- }
322
-
323
- throw system_error (make_error_code (errc::no_such_device), _path+name);
402
+ writer_cache (_path + name).put_string (value);
324
403
}
325
404
326
405
// -----------------------------------------------------------------------------
@@ -330,14 +409,7 @@ std::string device::get_attr_line(const std::string &name) const {
330
409
if (_path.empty ())
331
410
throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
332
411
333
- ifstream &is = ifstream_open (_path + name);
334
- if (is.is_open ()) {
335
- string result;
336
- getline (is, result);
337
- return result;
338
- }
339
-
340
- throw system_error (make_error_code (errc::no_such_device), _path+name);
412
+ return reader_cache (_path + name).get_line ();
341
413
}
342
414
343
415
// -----------------------------------------------------------------------------
@@ -513,14 +585,8 @@ const std::vector<char>& sensor::bin_data() const {
513
585
_bin_data.resize (num_values () * value_size);
514
586
}
515
587
516
- const string fname = _path + " bin_data" ;
517
- ifstream &is = ifstream_open (fname);
518
- if (is.is_open ()) {
519
- is.read (_bin_data.data (), _bin_data.size ());
520
- return _bin_data;
521
- }
522
-
523
- throw system_error (make_error_code (errc::no_such_device), fname);
588
+ reader_cache (_path + " bin_data" ).get_data (_bin_data.data (), _bin_data.size ());
589
+ return _bin_data;
524
590
}
525
591
526
592
// -----------------------------------------------------------------------------
0 commit comments