leveldb-ruby 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. data/README +17 -0
  2. data/ext/leveldb/extconf.rb +10 -0
  3. data/ext/leveldb/leveldb.cc +181 -0
  4. data/leveldb/Makefile +172 -0
  5. data/leveldb/db/builder.cc +90 -0
  6. data/leveldb/db/builder.h +36 -0
  7. data/leveldb/db/corruption_test.cc +354 -0
  8. data/leveldb/db/db_bench.cc +677 -0
  9. data/leveldb/db/db_impl.cc +1236 -0
  10. data/leveldb/db/db_impl.h +180 -0
  11. data/leveldb/db/db_iter.cc +298 -0
  12. data/leveldb/db/db_iter.h +26 -0
  13. data/leveldb/db/db_test.cc +1192 -0
  14. data/leveldb/db/dbformat.cc +87 -0
  15. data/leveldb/db/dbformat.h +165 -0
  16. data/leveldb/db/dbformat_test.cc +112 -0
  17. data/leveldb/db/filename.cc +135 -0
  18. data/leveldb/db/filename.h +80 -0
  19. data/leveldb/db/filename_test.cc +122 -0
  20. data/leveldb/db/log_format.h +35 -0
  21. data/leveldb/db/log_reader.cc +254 -0
  22. data/leveldb/db/log_reader.h +108 -0
  23. data/leveldb/db/log_test.cc +500 -0
  24. data/leveldb/db/log_writer.cc +103 -0
  25. data/leveldb/db/log_writer.h +48 -0
  26. data/leveldb/db/memtable.cc +108 -0
  27. data/leveldb/db/memtable.h +85 -0
  28. data/leveldb/db/repair.cc +384 -0
  29. data/leveldb/db/skiplist.h +378 -0
  30. data/leveldb/db/skiplist_test.cc +378 -0
  31. data/leveldb/db/snapshot.h +66 -0
  32. data/leveldb/db/table_cache.cc +95 -0
  33. data/leveldb/db/table_cache.h +50 -0
  34. data/leveldb/db/version_edit.cc +268 -0
  35. data/leveldb/db/version_edit.h +106 -0
  36. data/leveldb/db/version_edit_test.cc +46 -0
  37. data/leveldb/db/version_set.cc +1060 -0
  38. data/leveldb/db/version_set.h +306 -0
  39. data/leveldb/db/write_batch.cc +138 -0
  40. data/leveldb/db/write_batch_internal.h +45 -0
  41. data/leveldb/db/write_batch_test.cc +89 -0
  42. data/leveldb/include/leveldb/cache.h +99 -0
  43. data/leveldb/include/leveldb/comparator.h +63 -0
  44. data/leveldb/include/leveldb/db.h +148 -0
  45. data/leveldb/include/leveldb/env.h +302 -0
  46. data/leveldb/include/leveldb/iterator.h +100 -0
  47. data/leveldb/include/leveldb/options.h +198 -0
  48. data/leveldb/include/leveldb/slice.h +109 -0
  49. data/leveldb/include/leveldb/status.h +100 -0
  50. data/leveldb/include/leveldb/table.h +70 -0
  51. data/leveldb/include/leveldb/table_builder.h +91 -0
  52. data/leveldb/include/leveldb/write_batch.h +64 -0
  53. data/leveldb/port/port.h +23 -0
  54. data/leveldb/port/port_android.cc +64 -0
  55. data/leveldb/port/port_android.h +150 -0
  56. data/leveldb/port/port_chromium.cc +80 -0
  57. data/leveldb/port/port_chromium.h +97 -0
  58. data/leveldb/port/port_example.h +115 -0
  59. data/leveldb/port/port_osx.cc +50 -0
  60. data/leveldb/port/port_osx.h +125 -0
  61. data/leveldb/port/port_posix.cc +50 -0
  62. data/leveldb/port/port_posix.h +94 -0
  63. data/leveldb/port/sha1_portable.cc +298 -0
  64. data/leveldb/port/sha1_portable.h +25 -0
  65. data/leveldb/port/sha1_test.cc +39 -0
  66. data/leveldb/port/win/stdint.h +24 -0
  67. data/leveldb/table/block.cc +263 -0
  68. data/leveldb/table/block.h +43 -0
  69. data/leveldb/table/block_builder.cc +109 -0
  70. data/leveldb/table/block_builder.h +57 -0
  71. data/leveldb/table/format.cc +131 -0
  72. data/leveldb/table/format.h +103 -0
  73. data/leveldb/table/iterator.cc +67 -0
  74. data/leveldb/table/iterator_wrapper.h +63 -0
  75. data/leveldb/table/merger.cc +197 -0
  76. data/leveldb/table/merger.h +26 -0
  77. data/leveldb/table/table.cc +175 -0
  78. data/leveldb/table/table_builder.cc +227 -0
  79. data/leveldb/table/table_test.cc +845 -0
  80. data/leveldb/table/two_level_iterator.cc +182 -0
  81. data/leveldb/table/two_level_iterator.h +34 -0
  82. data/leveldb/util/arena.cc +68 -0
  83. data/leveldb/util/arena.h +68 -0
  84. data/leveldb/util/arena_test.cc +68 -0
  85. data/leveldb/util/cache.cc +255 -0
  86. data/leveldb/util/cache_test.cc +169 -0
  87. data/leveldb/util/coding.cc +194 -0
  88. data/leveldb/util/coding.h +104 -0
  89. data/leveldb/util/coding_test.cc +173 -0
  90. data/leveldb/util/comparator.cc +72 -0
  91. data/leveldb/util/crc32c.cc +332 -0
  92. data/leveldb/util/crc32c.h +45 -0
  93. data/leveldb/util/crc32c_test.cc +72 -0
  94. data/leveldb/util/env.cc +77 -0
  95. data/leveldb/util/env_chromium.cc +612 -0
  96. data/leveldb/util/env_posix.cc +606 -0
  97. data/leveldb/util/env_test.cc +102 -0
  98. data/leveldb/util/hash.cc +45 -0
  99. data/leveldb/util/hash.h +19 -0
  100. data/leveldb/util/histogram.cc +128 -0
  101. data/leveldb/util/histogram.h +41 -0
  102. data/leveldb/util/logging.cc +81 -0
  103. data/leveldb/util/logging.h +47 -0
  104. data/leveldb/util/mutexlock.h +39 -0
  105. data/leveldb/util/options.cc +28 -0
  106. data/leveldb/util/random.h +59 -0
  107. data/leveldb/util/status.cc +75 -0
  108. data/leveldb/util/testharness.cc +65 -0
  109. data/leveldb/util/testharness.h +129 -0
  110. data/leveldb/util/testutil.cc +51 -0
  111. data/leveldb/util/testutil.h +53 -0
  112. data/lib/leveldb.rb +36 -0
  113. metadata +183 -0
@@ -0,0 +1,606 @@
1
+ // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
+
5
+ #include <deque>
6
+ #include <dirent.h>
7
+ #include <errno.h>
8
+ #include <fcntl.h>
9
+ #include <pthread.h>
10
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+ #include <sys/mman.h>
14
+ #include <sys/stat.h>
15
+ #include <sys/time.h>
16
+ #include <sys/types.h>
17
+ #include <time.h>
18
+ #include <unistd.h>
19
+ #if defined(LEVELDB_PLATFORM_ANDROID)
20
+ #include <sys/stat.h>
21
+ #endif
22
+ #include "leveldb/env.h"
23
+ #include "leveldb/slice.h"
24
+ #include "port/port.h"
25
+ #include "util/logging.h"
26
+
27
+ namespace leveldb {
28
+
29
+ namespace {
30
+
31
+ class PosixSequentialFile: public SequentialFile {
32
+ private:
33
+ std::string filename_;
34
+ FILE* file_;
35
+
36
+ public:
37
+ PosixSequentialFile(const std::string& fname, FILE* f)
38
+ : filename_(fname), file_(f) { }
39
+ virtual ~PosixSequentialFile() { fclose(file_); }
40
+
41
+ virtual Status Read(size_t n, Slice* result, char* scratch) {
42
+ Status s;
43
+ size_t r = fread_unlocked(scratch, 1, n, file_);
44
+ *result = Slice(scratch, r);
45
+ if (r < n) {
46
+ if (feof(file_)) {
47
+ // We leave status as ok if we hit the end of the file
48
+ } else {
49
+ // A partial read with an error: return a non-ok status
50
+ s = Status::IOError(filename_, strerror(errno));
51
+ }
52
+ }
53
+ return s;
54
+ }
55
+
56
+ virtual Status Skip(uint64_t n) {
57
+ if (fseek(file_, n, SEEK_CUR)) {
58
+ return Status::IOError(filename_, strerror(errno));
59
+ }
60
+ return Status::OK();
61
+ }
62
+ };
63
+
64
+ class PosixRandomAccessFile: public RandomAccessFile {
65
+ private:
66
+ std::string filename_;
67
+ int fd_;
68
+
69
+ public:
70
+ PosixRandomAccessFile(const std::string& fname, int fd)
71
+ : filename_(fname), fd_(fd) { }
72
+ virtual ~PosixRandomAccessFile() { close(fd_); }
73
+
74
+ virtual Status Read(uint64_t offset, size_t n, Slice* result,
75
+ char* scratch) const {
76
+ Status s;
77
+ ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset));
78
+ *result = Slice(scratch, (r < 0) ? 0 : r);
79
+ if (r < 0) {
80
+ // An error: return a non-ok status
81
+ s = Status::IOError(filename_, strerror(errno));
82
+ }
83
+ return s;
84
+ }
85
+ };
86
+
87
+ // We preallocate up to an extra megabyte and use memcpy to append new
88
+ // data to the file. This is safe since we either properly close the
89
+ // file before reading from it, or for log files, the reading code
90
+ // knows enough to skip zero suffixes.
91
+ class PosixMmapFile : public WritableFile {
92
+ private:
93
+ std::string filename_;
94
+ int fd_;
95
+ size_t page_size_;
96
+ size_t map_size_; // How much extra memory to map at a time
97
+ char* base_; // The mapped region
98
+ char* limit_; // Limit of the mapped region
99
+ char* dst_; // Where to write next (in range [base_,limit_])
100
+ char* last_sync_; // Where have we synced up to
101
+ uint64_t file_offset_; // Offset of base_ in file
102
+
103
+ // Have we done an munmap of unsynced data?
104
+ bool pending_sync_;
105
+
106
+ // Roundup x to a multiple of y
107
+ static size_t Roundup(size_t x, size_t y) {
108
+ return ((x + y - 1) / y) * y;
109
+ }
110
+
111
+ size_t TruncateToPageBoundary(size_t s) {
112
+ s -= (s & (page_size_ - 1));
113
+ assert((s % page_size_) == 0);
114
+ return s;
115
+ }
116
+
117
+ void UnmapCurrentRegion() {
118
+ if (base_ != NULL) {
119
+ if (last_sync_ < limit_) {
120
+ // Defer syncing this data until next Sync() call, if any
121
+ pending_sync_ = true;
122
+ }
123
+ munmap(base_, limit_ - base_);
124
+ file_offset_ += limit_ - base_;
125
+ base_ = NULL;
126
+ limit_ = NULL;
127
+ last_sync_ = NULL;
128
+ dst_ = NULL;
129
+
130
+ // Increase the amount we map the next time, but capped at 1MB
131
+ if (map_size_ < (1<<20)) {
132
+ map_size_ *= 2;
133
+ }
134
+ }
135
+ }
136
+
137
+ bool MapNewRegion() {
138
+ assert(base_ == NULL);
139
+ if (ftruncate(fd_, file_offset_ + map_size_) < 0) {
140
+ return false;
141
+ }
142
+ void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED,
143
+ fd_, file_offset_);
144
+ if (ptr == MAP_FAILED) {
145
+ return false;
146
+ }
147
+ base_ = reinterpret_cast<char*>(ptr);
148
+ limit_ = base_ + map_size_;
149
+ dst_ = base_;
150
+ last_sync_ = base_;
151
+ return true;
152
+ }
153
+
154
+ public:
155
+ PosixMmapFile(const std::string& fname, int fd, size_t page_size)
156
+ : filename_(fname),
157
+ fd_(fd),
158
+ page_size_(page_size),
159
+ map_size_(Roundup(65536, page_size)),
160
+ base_(NULL),
161
+ limit_(NULL),
162
+ dst_(NULL),
163
+ last_sync_(NULL),
164
+ file_offset_(0),
165
+ pending_sync_(false) {
166
+ assert((page_size & (page_size - 1)) == 0);
167
+ }
168
+
169
+
170
+ ~PosixMmapFile() {
171
+ if (fd_ >= 0) {
172
+ PosixMmapFile::Close();
173
+ }
174
+ }
175
+
176
+ virtual Status Append(const Slice& data) {
177
+ const char* src = data.data();
178
+ size_t left = data.size();
179
+ while (left > 0) {
180
+ assert(base_ <= dst_);
181
+ assert(dst_ <= limit_);
182
+ size_t avail = limit_ - dst_;
183
+ if (avail == 0) {
184
+ UnmapCurrentRegion();
185
+ MapNewRegion();
186
+ }
187
+
188
+ size_t n = (left <= avail) ? left : avail;
189
+ memcpy(dst_, src, n);
190
+ dst_ += n;
191
+ src += n;
192
+ left -= n;
193
+ }
194
+ return Status::OK();
195
+ }
196
+
197
+ virtual Status Close() {
198
+ Status s;
199
+ size_t unused = limit_ - dst_;
200
+ UnmapCurrentRegion();
201
+ if (unused > 0) {
202
+ // Trim the extra space at the end of the file
203
+ if (ftruncate(fd_, file_offset_ - unused) < 0) {
204
+ s = Status::IOError(filename_, strerror(errno));
205
+ }
206
+ }
207
+
208
+ if (close(fd_) < 0) {
209
+ if (s.ok()) {
210
+ s = Status::IOError(filename_, strerror(errno));
211
+ }
212
+ }
213
+
214
+ fd_ = -1;
215
+ base_ = NULL;
216
+ limit_ = NULL;
217
+ return s;
218
+ }
219
+
220
+ virtual Status Flush() {
221
+ return Status::OK();
222
+ }
223
+
224
+ virtual Status Sync() {
225
+ Status s;
226
+
227
+ if (pending_sync_) {
228
+ // Some unmapped data was not synced
229
+ pending_sync_ = false;
230
+ if (fdatasync(fd_) < 0) {
231
+ s = Status::IOError(filename_, strerror(errno));
232
+ }
233
+ }
234
+
235
+ if (dst_ > last_sync_) {
236
+ // Find the beginnings of the pages that contain the first and last
237
+ // bytes to be synced.
238
+ size_t p1 = TruncateToPageBoundary(last_sync_ - base_);
239
+ size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1);
240
+ last_sync_ = dst_;
241
+ if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) {
242
+ s = Status::IOError(filename_, strerror(errno));
243
+ }
244
+ }
245
+
246
+ return s;
247
+ }
248
+ };
249
+
250
+ static int LockOrUnlock(int fd, bool lock) {
251
+ errno = 0;
252
+ struct flock f;
253
+ memset(&f, 0, sizeof(f));
254
+ f.l_type = (lock ? F_WRLCK : F_UNLCK);
255
+ f.l_whence = SEEK_SET;
256
+ f.l_start = 0;
257
+ f.l_len = 0; // Lock/unlock entire file
258
+ return fcntl(fd, F_SETLK, &f);
259
+ }
260
+
261
+ class PosixFileLock : public FileLock {
262
+ public:
263
+ int fd_;
264
+ };
265
+
266
+ class PosixEnv : public Env {
267
+ public:
268
+ PosixEnv();
269
+ virtual ~PosixEnv() {
270
+ fprintf(stderr, "Destroying Env::Default()\n");
271
+ exit(1);
272
+ }
273
+
274
+ virtual Status NewSequentialFile(const std::string& fname,
275
+ SequentialFile** result) {
276
+ FILE* f = fopen(fname.c_str(), "r");
277
+ if (f == NULL) {
278
+ *result = NULL;
279
+ return Status::IOError(fname, strerror(errno));
280
+ } else {
281
+ *result = new PosixSequentialFile(fname, f);
282
+ return Status::OK();
283
+ }
284
+ }
285
+
286
+ virtual Status NewRandomAccessFile(const std::string& fname,
287
+ RandomAccessFile** result) {
288
+ int fd = open(fname.c_str(), O_RDONLY);
289
+ if (fd < 0) {
290
+ *result = NULL;
291
+ return Status::IOError(fname, strerror(errno));
292
+ }
293
+ *result = new PosixRandomAccessFile(fname, fd);
294
+ return Status::OK();
295
+ }
296
+
297
+ virtual Status NewWritableFile(const std::string& fname,
298
+ WritableFile** result) {
299
+ Status s;
300
+ const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
301
+ if (fd < 0) {
302
+ *result = NULL;
303
+ s = Status::IOError(fname, strerror(errno));
304
+ } else {
305
+ *result = new PosixMmapFile(fname, fd, page_size_);
306
+ }
307
+ return s;
308
+ }
309
+
310
+ virtual bool FileExists(const std::string& fname) {
311
+ return access(fname.c_str(), F_OK) == 0;
312
+ }
313
+
314
+ virtual Status GetChildren(const std::string& dir,
315
+ std::vector<std::string>* result) {
316
+ result->clear();
317
+ DIR* d = opendir(dir.c_str());
318
+ if (d == NULL) {
319
+ return Status::IOError(dir, strerror(errno));
320
+ }
321
+ struct dirent* entry;
322
+ while ((entry = readdir(d)) != NULL) {
323
+ result->push_back(entry->d_name);
324
+ }
325
+ closedir(d);
326
+ return Status::OK();
327
+ }
328
+
329
+ virtual Status DeleteFile(const std::string& fname) {
330
+ Status result;
331
+ if (unlink(fname.c_str()) != 0) {
332
+ result = Status::IOError(fname, strerror(errno));
333
+ }
334
+ return result;
335
+ };
336
+
337
+ virtual Status CreateDir(const std::string& name) {
338
+ Status result;
339
+ if (mkdir(name.c_str(), 0755) != 0) {
340
+ result = Status::IOError(name, strerror(errno));
341
+ }
342
+ return result;
343
+ };
344
+
345
+ virtual Status DeleteDir(const std::string& name) {
346
+ Status result;
347
+ if (rmdir(name.c_str()) != 0) {
348
+ result = Status::IOError(name, strerror(errno));
349
+ }
350
+ return result;
351
+ };
352
+
353
+ virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
354
+ Status s;
355
+ struct stat sbuf;
356
+ if (stat(fname.c_str(), &sbuf) != 0) {
357
+ *size = 0;
358
+ s = Status::IOError(fname, strerror(errno));
359
+ } else {
360
+ *size = sbuf.st_size;
361
+ }
362
+ return s;
363
+ }
364
+
365
+ virtual Status RenameFile(const std::string& src, const std::string& target) {
366
+ Status result;
367
+ if (rename(src.c_str(), target.c_str()) != 0) {
368
+ result = Status::IOError(src, strerror(errno));
369
+ }
370
+ return result;
371
+ }
372
+
373
+ virtual Status LockFile(const std::string& fname, FileLock** lock) {
374
+ *lock = NULL;
375
+ Status result;
376
+ int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644);
377
+ if (fd < 0) {
378
+ result = Status::IOError(fname, strerror(errno));
379
+ } else if (LockOrUnlock(fd, true) == -1) {
380
+ result = Status::IOError("lock " + fname, strerror(errno));
381
+ close(fd);
382
+ } else {
383
+ PosixFileLock* my_lock = new PosixFileLock;
384
+ my_lock->fd_ = fd;
385
+ *lock = my_lock;
386
+ }
387
+ return result;
388
+ }
389
+
390
+ virtual Status UnlockFile(FileLock* lock) {
391
+ PosixFileLock* my_lock = reinterpret_cast<PosixFileLock*>(lock);
392
+ Status result;
393
+ if (LockOrUnlock(my_lock->fd_, false) == -1) {
394
+ result = Status::IOError(strerror(errno));
395
+ }
396
+ close(my_lock->fd_);
397
+ delete my_lock;
398
+ return result;
399
+ }
400
+
401
+ virtual void Schedule(void (*function)(void*), void* arg);
402
+
403
+ virtual void StartThread(void (*function)(void* arg), void* arg);
404
+
405
+ virtual Status GetTestDirectory(std::string* result) {
406
+ const char* env = getenv("TEST_TMPDIR");
407
+ if (env && env[0] != '\0') {
408
+ *result = env;
409
+ } else {
410
+ char buf[100];
411
+ snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));
412
+ *result = buf;
413
+ }
414
+ // Directory may already exist
415
+ CreateDir(*result);
416
+ return Status::OK();
417
+ }
418
+
419
+ virtual void Logv(WritableFile* info_log, const char* format, va_list ap) {
420
+ pthread_t tid = pthread_self();
421
+ uint64_t thread_id = 0;
422
+ memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid)));
423
+
424
+ // We try twice: the first time with a fixed-size stack allocated buffer,
425
+ // and the second time with a much larger dynamically allocated buffer.
426
+ char buffer[500];
427
+ for (int iter = 0; iter < 2; iter++) {
428
+ char* base;
429
+ int bufsize;
430
+ if (iter == 0) {
431
+ bufsize = sizeof(buffer);
432
+ base = buffer;
433
+ } else {
434
+ bufsize = 30000;
435
+ base = new char[bufsize];
436
+ }
437
+ char* p = base;
438
+ char* limit = base + bufsize;
439
+
440
+ struct timeval now_tv;
441
+ gettimeofday(&now_tv, NULL);
442
+ const time_t seconds = now_tv.tv_sec;
443
+ struct tm t;
444
+ localtime_r(&seconds, &t);
445
+ p += snprintf(p, limit - p,
446
+ "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
447
+ t.tm_year + 1900,
448
+ t.tm_mon + 1,
449
+ t.tm_mday,
450
+ t.tm_hour,
451
+ t.tm_min,
452
+ t.tm_sec,
453
+ static_cast<int>(now_tv.tv_usec),
454
+ static_cast<long long unsigned int>(thread_id));
455
+
456
+ // Print the message
457
+ if (p < limit) {
458
+ va_list backup_ap;
459
+ va_copy(backup_ap, ap);
460
+ p += vsnprintf(p, limit - p, format, backup_ap);
461
+ va_end(backup_ap);
462
+ }
463
+
464
+ // Truncate to available space if necessary
465
+ if (p >= limit) {
466
+ if (iter == 0) {
467
+ continue; // Try again with larger buffer
468
+ } else {
469
+ p = limit - 1;
470
+ }
471
+ }
472
+
473
+ // Add newline if necessary
474
+ if (p == base || p[-1] != '\n') {
475
+ *p++ = '\n';
476
+ }
477
+
478
+ assert(p <= limit);
479
+ info_log->Append(Slice(base, p - base));
480
+ info_log->Flush();
481
+ if (base != buffer) {
482
+ delete[] base;
483
+ }
484
+ break;
485
+ }
486
+ }
487
+
488
+ virtual uint64_t NowMicros() {
489
+ struct timeval tv;
490
+ gettimeofday(&tv, NULL);
491
+ return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
492
+ }
493
+
494
+ virtual void SleepForMicroseconds(int micros) {
495
+ usleep(micros);
496
+ }
497
+
498
+ private:
499
+ void PthreadCall(const char* label, int result) {
500
+ if (result != 0) {
501
+ fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
502
+ exit(1);
503
+ }
504
+ }
505
+
506
+ // BGThread() is the body of the background thread
507
+ void BGThread();
508
+ static void* BGThreadWrapper(void* arg) {
509
+ reinterpret_cast<PosixEnv*>(arg)->BGThread();
510
+ return NULL;
511
+ }
512
+
513
+ size_t page_size_;
514
+ pthread_mutex_t mu_;
515
+ pthread_cond_t bgsignal_;
516
+ pthread_t bgthread_;
517
+ bool started_bgthread_;
518
+
519
+ // Entry per Schedule() call
520
+ struct BGItem { void* arg; void (*function)(void*); };
521
+ typedef std::deque<BGItem> BGQueue;
522
+ BGQueue queue_;
523
+ };
524
+
525
+ PosixEnv::PosixEnv() : page_size_(getpagesize()),
526
+ started_bgthread_(false) {
527
+ PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL));
528
+ PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL));
529
+ }
530
+
531
+ void PosixEnv::Schedule(void (*function)(void*), void* arg) {
532
+ PthreadCall("lock", pthread_mutex_lock(&mu_));
533
+
534
+ // Start background thread if necessary
535
+ if (!started_bgthread_) {
536
+ started_bgthread_ = true;
537
+ PthreadCall(
538
+ "create thread",
539
+ pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this));
540
+ }
541
+
542
+ // If the queue is currently empty, the background thread may currently be
543
+ // waiting.
544
+ if (queue_.empty()) {
545
+ PthreadCall("signal", pthread_cond_signal(&bgsignal_));
546
+ }
547
+
548
+ // Add to priority queue
549
+ queue_.push_back(BGItem());
550
+ queue_.back().function = function;
551
+ queue_.back().arg = arg;
552
+
553
+ PthreadCall("unlock", pthread_mutex_unlock(&mu_));
554
+ }
555
+
556
+ void PosixEnv::BGThread() {
557
+ while (true) {
558
+ // Wait until there is an item that is ready to run
559
+ PthreadCall("lock", pthread_mutex_lock(&mu_));
560
+ while (queue_.empty()) {
561
+ PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_));
562
+ }
563
+
564
+ void (*function)(void*) = queue_.front().function;
565
+ void* arg = queue_.front().arg;
566
+ queue_.pop_front();
567
+
568
+ PthreadCall("unlock", pthread_mutex_unlock(&mu_));
569
+ (*function)(arg);
570
+ }
571
+ }
572
+
573
+ namespace {
574
+ struct StartThreadState {
575
+ void (*user_function)(void*);
576
+ void* arg;
577
+ };
578
+ }
579
+ static void* StartThreadWrapper(void* arg) {
580
+ StartThreadState* state = reinterpret_cast<StartThreadState*>(arg);
581
+ state->user_function(state->arg);
582
+ delete state;
583
+ return NULL;
584
+ }
585
+
586
+ void PosixEnv::StartThread(void (*function)(void* arg), void* arg) {
587
+ pthread_t t;
588
+ StartThreadState* state = new StartThreadState;
589
+ state->user_function = function;
590
+ state->arg = arg;
591
+ PthreadCall("start thread",
592
+ pthread_create(&t, NULL, &StartThreadWrapper, state));
593
+ }
594
+
595
+ }
596
+
597
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
598
+ static Env* default_env;
599
+ static void InitDefaultEnv() { default_env = new PosixEnv; }
600
+
601
+ Env* Env::Default() {
602
+ pthread_once(&once, InitDefaultEnv);
603
+ return default_env;
604
+ }
605
+
606
+ }