10
10
#include < stdlib.h>
11
11
#include < string.h>
12
12
#include < sys/mman.h>
13
+ #include < sys/resource.h>
13
14
#include < sys/stat.h>
14
15
#include < sys/time.h>
15
16
#include < sys/types.h>
23
24
#include " util/logging.h"
24
25
#include " util/mutexlock.h"
25
26
#include " util/posix_logger.h"
27
+ #include " util/env_posix_test_helper.h"
26
28
27
29
namespace leveldb {
28
30
29
31
namespace {
30
32
33
+ static int open_read_only_file_limit = -1 ;
34
+ static int mmap_limit = -1 ;
35
+
31
36
static Status IOError (const std::string& context, int err_number) {
32
37
return Status::IOError (context, strerror (err_number));
33
38
}
34
39
40
+ // Helper class to limit resource usage to avoid exhaustion.
41
+ // Currently used to limit read-only file descriptors and mmap file usage
42
+ // so that we do not end up running out of file descriptors, virtual memory,
43
+ // or running into kernel performance problems for very large databases.
44
+ class Limiter {
45
+ public:
46
+ // Limit maximum number of resources to |n|.
47
+ Limiter (intptr_t n) {
48
+ SetAllowed (n);
49
+ }
50
+
51
+ // If another resource is available, acquire it and return true.
52
+ // Else return false.
53
+ bool Acquire () {
54
+ if (GetAllowed () <= 0 ) {
55
+ return false ;
56
+ }
57
+ MutexLock l (&mu_);
58
+ intptr_t x = GetAllowed ();
59
+ if (x <= 0 ) {
60
+ return false ;
61
+ } else {
62
+ SetAllowed (x - 1 );
63
+ return true ;
64
+ }
65
+ }
66
+
67
+ // Release a resource acquired by a previous call to Acquire() that returned
68
+ // true.
69
+ void Release () {
70
+ MutexLock l (&mu_);
71
+ SetAllowed (GetAllowed () + 1 );
72
+ }
73
+
74
+ private:
75
+ port::Mutex mu_;
76
+ port::AtomicPointer allowed_;
77
+
78
+ intptr_t GetAllowed () const {
79
+ return reinterpret_cast <intptr_t >(allowed_.Acquire_Load ());
80
+ }
81
+
82
+ // REQUIRES: mu_ must be held
83
+ void SetAllowed (intptr_t v) {
84
+ allowed_.Release_Store (reinterpret_cast <void *>(v));
85
+ }
86
+
87
+ Limiter (const Limiter&);
88
+ void operator =(const Limiter&);
89
+ };
90
+
35
91
class PosixSequentialFile : public SequentialFile {
36
92
private:
37
93
std::string filename_;
@@ -69,73 +125,51 @@ class PosixSequentialFile: public SequentialFile {
69
125
class PosixRandomAccessFile : public RandomAccessFile {
70
126
private:
71
127
std::string filename_;
128
+ bool temporary_fd_; // If true, fd_ is -1 and we open on every read.
72
129
int fd_;
130
+ Limiter* limiter_;
73
131
74
132
public:
75
- PosixRandomAccessFile (const std::string& fname, int fd)
76
- : filename_(fname), fd_(fd) { }
77
- virtual ~PosixRandomAccessFile () { close (fd_); }
133
+ PosixRandomAccessFile (const std::string& fname, int fd, Limiter* limiter)
134
+ : filename_(fname), fd_(fd), limiter_(limiter) {
135
+ temporary_fd_ = !limiter->Acquire ();
136
+ if (temporary_fd_) {
137
+ // Open file on every access.
138
+ close (fd_);
139
+ fd_ = -1 ;
140
+ }
141
+ }
142
+
143
+ virtual ~PosixRandomAccessFile () {
144
+ if (!temporary_fd_) {
145
+ close (fd_);
146
+ limiter_->Release ();
147
+ }
148
+ }
78
149
79
150
virtual Status Read (uint64_t offset, size_t n, Slice* result,
80
151
char * scratch) const {
152
+ int fd = fd_;
153
+ if (temporary_fd_) {
154
+ fd = open (filename_.c_str (), O_RDONLY);
155
+ if (fd < 0 ) {
156
+ return IOError (filename_, errno);
157
+ }
158
+ }
159
+
81
160
Status s;
82
- ssize_t r = pread (fd_ , scratch, n, static_cast <off_t >(offset));
161
+ ssize_t r = pread (fd , scratch, n, static_cast <off_t >(offset));
83
162
*result = Slice (scratch, (r < 0 ) ? 0 : r);
84
163
if (r < 0 ) {
85
164
// An error: return a non-ok status
86
165
s = IOError (filename_, errno);
87
166
}
88
- return s;
89
- }
90
- };
91
-
92
- // Helper class to limit mmap file usage so that we do not end up
93
- // running out virtual memory or running into kernel performance
94
- // problems for very large databases.
95
- class MmapLimiter {
96
- public:
97
- // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes.
98
- MmapLimiter () {
99
- SetAllowed (sizeof (void *) >= 8 ? 1000 : 0 );
100
- }
101
-
102
- // If another mmap slot is available, acquire it and return true.
103
- // Else return false.
104
- bool Acquire () {
105
- if (GetAllowed () <= 0 ) {
106
- return false ;
107
- }
108
- MutexLock l (&mu_);
109
- intptr_t x = GetAllowed ();
110
- if (x <= 0 ) {
111
- return false ;
112
- } else {
113
- SetAllowed (x - 1 );
114
- return true ;
167
+ if (temporary_fd_) {
168
+ // Close the temporary file descriptor opened earlier.
169
+ close (fd);
115
170
}
171
+ return s;
116
172
}
117
-
118
- // Release a slot acquired by a previous call to Acquire() that returned true.
119
- void Release () {
120
- MutexLock l (&mu_);
121
- SetAllowed (GetAllowed () + 1 );
122
- }
123
-
124
- private:
125
- port::Mutex mu_;
126
- port::AtomicPointer allowed_;
127
-
128
- intptr_t GetAllowed () const {
129
- return reinterpret_cast <intptr_t >(allowed_.Acquire_Load ());
130
- }
131
-
132
- // REQUIRES: mu_ must be held
133
- void SetAllowed (intptr_t v) {
134
- allowed_.Release_Store (reinterpret_cast <void *>(v));
135
- }
136
-
137
- MmapLimiter (const MmapLimiter&);
138
- void operator =(const MmapLimiter&);
139
173
};
140
174
141
175
// mmap() based random-access
@@ -144,12 +178,12 @@ class PosixMmapReadableFile: public RandomAccessFile {
144
178
std::string filename_;
145
179
void * mmapped_region_;
146
180
size_t length_;
147
- MmapLimiter * limiter_;
181
+ Limiter * limiter_;
148
182
149
183
public:
150
184
// base[0,length-1] contains the mmapped contents of the file.
151
185
PosixMmapReadableFile (const std::string& fname, void * base, size_t length,
152
- MmapLimiter * limiter)
186
+ Limiter * limiter)
153
187
: filename_(fname), mmapped_region_(base), length_(length),
154
188
limiter_ (limiter) {
155
189
}
@@ -332,7 +366,7 @@ class PosixEnv : public Env {
332
366
mmap_limit_.Release ();
333
367
}
334
368
} else {
335
- *result = new PosixRandomAccessFile (fname, fd);
369
+ *result = new PosixRandomAccessFile (fname, fd, &fd_limit_ );
336
370
}
337
371
return s;
338
372
}
@@ -532,10 +566,42 @@ class PosixEnv : public Env {
532
566
BGQueue queue_;
533
567
534
568
PosixLockTable locks_;
535
- MmapLimiter mmap_limit_;
569
+ Limiter mmap_limit_;
570
+ Limiter fd_limit_;
536
571
};
537
572
538
- PosixEnv::PosixEnv () : started_bgthread_(false ) {
573
+ // Return the maximum number of concurrent mmaps.
574
+ static int MaxMmaps () {
575
+ if (mmap_limit >= 0 ) {
576
+ return mmap_limit;
577
+ }
578
+ // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes.
579
+ mmap_limit = sizeof (void *) >= 8 ? 1000 : 0 ;
580
+ return mmap_limit;
581
+ }
582
+
583
+ // Return the maximum number of read-only files to keep open.
584
+ static intptr_t MaxOpenFiles () {
585
+ if (open_read_only_file_limit >= 0 ) {
586
+ return open_read_only_file_limit;
587
+ }
588
+ struct rlimit rlim;
589
+ if (getrlimit (RLIMIT_NOFILE, &rlim)) {
590
+ // getrlimit failed, fallback to hard-coded default.
591
+ open_read_only_file_limit = 50 ;
592
+ } else if (rlim.rlim_cur == RLIM_INFINITY) {
593
+ open_read_only_file_limit = std::numeric_limits<int >::max ();
594
+ } else {
595
+ // Allow use of 20% of available file descriptors for read-only files.
596
+ open_read_only_file_limit = rlim.rlim_cur / 5 ;
597
+ }
598
+ return open_read_only_file_limit;
599
+ }
600
+
601
+ PosixEnv::PosixEnv ()
602
+ : started_bgthread_(false ),
603
+ mmap_limit_ (MaxMmaps()),
604
+ fd_limit_(MaxOpenFiles()) {
539
605
PthreadCall (" mutex_init" , pthread_mutex_init (&mu_, NULL ));
540
606
PthreadCall (" cvar_init" , pthread_cond_init (&bgsignal_, NULL ));
541
607
}
@@ -610,6 +676,16 @@ static pthread_once_t once = PTHREAD_ONCE_INIT;
610
676
static Env* default_env;
611
677
static void InitDefaultEnv () { default_env = new PosixEnv; }
612
678
679
+ void EnvPosixTestHelper::SetReadOnlyFDLimit (int limit) {
680
+ assert (default_env == NULL );
681
+ open_read_only_file_limit = limit;
682
+ }
683
+
684
+ void EnvPosixTestHelper::SetReadOnlyMMapLimit (int limit) {
685
+ assert (default_env == NULL );
686
+ mmap_limit = limit;
687
+ }
688
+
613
689
Env* Env::Default () {
614
690
pthread_once (&once, InitDefaultEnv);
615
691
return default_env;
0 commit comments