leveldb-ruby 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }