fix ruby/thread.h checking under 1.9.3
[ruby-tdb.git] / ext / tdb / tdb.c
blob2419befdd9cc94bbf8038f17fcd117183236cd54
1 #include "rbtdb.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #include <pthread.h>
8 /* this protects the global list of tdb objects maintained by libtdb */
9 static pthread_mutex_t big_lock = PTHREAD_MUTEX_INITIALIZER;
10 static VALUE cTDB, cERR;
11 static VALUE exc_hash;
12 static VALUE hashes;
14 /* must be a macro to prevent GC from killing converted 'val's */
15 #define TO_TDB_DATA(data,val) do { \
16 StringValue(val); \
17 (data).dptr = (unsigned char *)RSTRING_PTR(val); \
18 (data).dsize = RSTRING_LEN(val); \
19 } while (0)
21 static void init_exc(enum TDB_ERROR ecode, const char *name)
23 VALUE exc = rb_define_class_under(cERR, name, cERR);
24 rb_hash_aset(exc_hash, INT2NUM(ecode), exc);
27 static void init_errors(void)
29 cERR = rb_define_class_under(cTDB, "ERR", rb_eStandardError);
30 exc_hash = rb_hash_new();
31 rb_global_variable(&exc_hash);
33 init_exc(TDB_ERR_CORRUPT, "CORRUPT");
34 init_exc(TDB_ERR_IO, "IO");
35 init_exc(TDB_ERR_LOCK, "LOCK");
36 init_exc(TDB_ERR_OOM, "OOM");
37 init_exc(TDB_ERR_EXISTS, "EXISTS"),
38 init_exc(TDB_ERR_NOLOCK, "NOLOCK");
39 init_exc(TDB_ERR_LOCK_TIMEOUT, "LOCK_TIMEOUT");
40 init_exc(TDB_ERR_EINVAL, "EINVAL");
41 init_exc(TDB_ERR_NOEXIST, "NOEXIST");
42 init_exc(TDB_ERR_RDONLY, "RDONLY");
43 #ifdef HAVE_CONST_TDB_ERR_NESTING
44 init_exc(TDB_ERR_NESTING, "NESTING");
45 #endif /* HAVE_CONST_TDB_ERR_NESTING */
48 static void my_raise(struct tdb_context *tdb)
50 enum TDB_ERROR ecode = tdb_error(tdb);
51 const char *str = tdb_errorstr(tdb);
52 VALUE exc = Qnil;
54 switch (ecode) {
55 case TDB_SUCCESS:
56 rb_bug("attempted to raise with no error");
57 case TDB_ERR_CORRUPT:
58 case TDB_ERR_IO:
59 case TDB_ERR_LOCK:
60 case TDB_ERR_OOM:
61 case TDB_ERR_EXISTS:
62 case TDB_ERR_NOLOCK:
63 case TDB_ERR_LOCK_TIMEOUT:
64 case TDB_ERR_EINVAL:
65 case TDB_ERR_NOEXIST:
66 case TDB_ERR_RDONLY:
67 #ifdef HAVE_CONST_TDB_ERR_NESTING
68 case TDB_ERR_NESTING:
69 #endif /* HAVE_CONST_TDB_ERR_NESTING */
70 exc = rb_hash_aref(exc_hash, INT2NUM(ecode));
72 if (NIL_P(exc))
73 rb_bug("no-existent exception: %s\n", str);
74 rb_raise(exc, "%s", str);
77 static void init_hashes(void)
79 #define HF(x) \
80 rb_hash_aset(hashes,ID2SYM(rb_intern(#x)),ULONG2NUM((unsigned long)rbtdb_##x))
81 HF(siphash24);
82 HF(murmur1);
83 HF(murmur1_aligned);
84 HF(murmur2);
85 HF(murmur2a);
86 HF(murmur2_neutral);
87 HF(murmur2_aligned);
88 HF(murmur3a);
89 HF(murmur3f);
90 HF(fnv1a);
91 HF(djb2);
92 HF(djb3);
93 HF(jenkins_lookup3);
94 HF(default);
97 #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) /* Ruby 2.1+ */
98 # include <ruby/thread.h>
99 # define WITHOUT_GVL(fn,a,ubf,b) \
100 rb_thread_call_without_gvl((fn),(a),(ubf),(b))
101 /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
102 #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) /* Ruby 1.9-2.0 */
103 typedef VALUE (*my_blocking_fn_t)(void*);
104 # define WITHOUT_GVL(fn,a,ubf,b) \
105 rb_thread_blocking_region((my_blocking_fn_t)(fn),(a),(ubf),(b))
106 #else /* Ruby 1.8 */
107 # include <rubysig.h>
108 # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
109 typedef void rb_unblock_function_t(void *);
110 typedef void * rb_blocking_function_t(void *);
111 static void * WITHOUT_GVL(rb_blocking_function_t *func, void *data1,
112 rb_unblock_function_t *ubf, void *data2)
114 void *rv;
116 TRAP_BEG;
117 rv = func(data1);
118 TRAP_END;
120 return rv;
122 #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
124 #define my_tbr(fn,data) WITHOUT_GVL((void *)(fn),(data),RUBY_UBF_IO,0)
126 static void gcfree(void *ptr)
128 struct tdb_context *tdb = ptr;
130 /* no error checking in GC :< */
131 if (tdb) {
132 (void)pthread_mutex_lock(&big_lock);
133 (void)tdb_close(tdb);
134 (void)pthread_mutex_unlock(&big_lock);
138 static VALUE alloc(VALUE klass)
140 return Data_Wrap_Struct(klass, NULL, gcfree, NULL);
143 static struct tdb_context *db(VALUE self, int check_opened)
145 struct tdb_context *tdb;
147 Data_Get_Struct(self, struct tdb_context, tdb);
149 if (!tdb && check_opened)
150 rb_raise(rb_eIOError, "closed database");
152 return tdb;
155 struct open_args {
156 const char *name;
157 int hash_size;
158 int tdb_flags;
159 int open_flags;
160 mode_t mode;
161 struct tdb_logging_context *log_ctx;
162 tdb_hash_func hash_fn;
165 static void * nogvl_open(void *ptr)
167 struct open_args *o = ptr;
168 struct tdb_context *tdb;
170 pthread_mutex_lock(&big_lock);
171 tdb = tdb_open_ex(o->name, o->hash_size, o->tdb_flags,
172 o->open_flags, o->mode, o->log_ctx, o->hash_fn);
173 pthread_mutex_unlock(&big_lock);
175 return (void *)tdb;
178 static void set_args(VALUE self, struct open_args *o, VALUE opts)
180 VALUE tmp;
182 o->name = NULL;
183 o->hash_size = 0; /* default */
184 o->tdb_flags = TDB_DEFAULT;
185 o->open_flags = O_RDWR | O_CREAT;
186 o->mode = 0666;
187 o->log_ctx = NULL;
188 o->hash_fn = NULL;
190 if (NIL_P(opts))
191 return;
192 Check_Type(opts, T_HASH);
194 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash_size")));
195 if (!NIL_P(tmp))
196 o->hash_size = NUM2INT(tmp);
198 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
199 if (!NIL_P(tmp))
200 o->mode = NUM2UINT(tmp);
202 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("open_flags")));
203 if (!NIL_P(tmp))
204 o->open_flags = NUM2INT(tmp);
206 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("tdb_flags")));
207 if (!NIL_P(tmp))
208 o->tdb_flags = NUM2INT(tmp);
210 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash")));
211 if (!NIL_P(tmp)) {
212 VALUE num = rb_hash_aref(hashes, tmp);
214 if (NIL_P(num)) {
215 tmp = rb_inspect(tmp);
216 rb_raise(rb_eArgError,
217 "`%s' is not a valid hash function",
218 StringValuePtr(tmp));
221 o->hash_fn = (tdb_hash_func)NUM2ULONG(num);
224 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("threadsafe")));
225 if (RTEST(tmp))
226 rb_funcall(self, rb_intern("threadsafe!"), 0);
230 * :call-seq:
232 * TDB.new("/path/to/file") -> TDB
233 * TDB.new("/path/to/file", :hash_size => 666) -> TDB
234 * TDB.new("/path/to/file", :hash => :murmur2) -> TDB
235 * TDB.new("/path/to/file", :open_flags => IO::RDONLY) -> TDB
236 * TDB.new("/path/to/file", :tdb_flags => TDB::NOSYNC) -> TDB
238 * Initializes a TDB context. It takes several options.
240 * :hash_size - the number of buckets, this is the most important tuning
241 * parameter when creating large databases. This parameter only affects
242 * the creation of new databases.
244 * :open_flags - a bit mask of IO flags passed directly to open(2),
245 * File.open-compatible flags are accepted.
247 * :hash - any of the hashes described in Hash_Functions.
248 * This must remain the same for all clients.
250 * :tdb_flags - a bitmask of any combination of TDB::CLEAR_IF_FIRST,
251 * TDB::INTERNAL, TDB::NOLOCK, TDB::NOMMAP, TDB::CONVERT,
252 * TDB::BIGENDIAN, TDB::NOSYNC, TDB::SEQNUM, TDB::VOLATILE,
253 * TDB::ALLOW_NESTING, TDB::DISALLOW_NESTING, TDB::INCOMPATIBLE_HASH
255 * :mode - octal mode mask passed to open(2)
257 static VALUE init(int argc, VALUE *argv, VALUE self)
259 struct tdb_context *tdb = db(self, 0);
260 VALUE path, opts;
261 struct open_args o;
263 if (tdb)
264 rb_raise(rb_eRuntimeError, "TDB already initialized");
265 rb_scan_args(argc, argv, "11", &path, &opts);
266 set_args(self, &o, opts);
268 if (NIL_P(path))
269 o.tdb_flags |= TDB_INTERNAL;
270 else
271 o.name = StringValueCStr(path);
273 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
274 if (!tdb) {
275 switch (errno) {
276 case ENOMEM:
277 case EMFILE:
278 case ENFILE:
279 rb_gc();
280 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
282 if (!tdb)
283 rb_sys_fail("tdb_open_ex");
285 DATA_PTR(self) = tdb;
287 return self;
290 /* tdb_close can do a lot, including cancel transactions an munmap */
291 static void * nogvl_close(void *ptr)
293 struct tdb_context *tdb = ptr;
294 long rv;
296 pthread_mutex_lock(&big_lock);
297 rv = tdb_close(tdb);
298 pthread_mutex_unlock(&big_lock);
300 return (void *)rv;
303 static VALUE tdbclose(VALUE self)
305 struct tdb_context *tdb = db(self, 1);
307 DATA_PTR(self) = NULL;
309 if ((long)my_tbr(nogvl_close, tdb) == -1)
310 rb_sys_fail("tdb_close");
312 return Qnil;
315 static VALUE closed(VALUE self)
317 struct tdb_context *tdb = db(self, 0);
319 return tdb ? Qfalse : Qtrue;
322 #ifdef HAVE_RB_THREAD_CALL_WITH_GVL
323 /* missing prototype in ruby.h: */
324 void *rb_thread_call_with_gvl(void *(*func)(void *), void *data);
325 #else
326 static void * my_rb_thread_call_with_gvl(void *(*func)(void *), void *data)
328 return (*func)(data);
330 #define rb_thread_call_with_gvl my_rb_thread_call_with_gvl
331 #endif /* !HAVE_RB_THREAD_CALL_WITH_GVL */
334 * We avoid the extra malloc/free pair enforced by tdb_fetch. We
335 * use tdb_parse_record to give us pointers to (hopefully) mmap-ed
336 * regions and create a String object directly off that region.
338 struct fetch_parse_args {
339 struct tdb_context *tdb;
340 union {
341 TDB_DATA key;
342 long value_len;
343 char *value_ptr;
344 VALUE value;
345 } as;
346 VALUE value;
349 static VALUE str_new_tdb_data(TDB_DATA *val)
351 return rb_str_new((const char *)val->dptr, val->dsize);
354 static void *gvl_str_resize(void *data)
356 struct fetch_parse_args *f = data;
358 rb_str_resize(f->value, f->as.value_len);
359 f->as.value_ptr = RSTRING_PTR(f->value);
361 return NULL;
364 static int fetch_parse(TDB_DATA key, TDB_DATA val, void *data)
366 struct fetch_parse_args *f = data;
368 f->as.value_len = val.dsize;
369 (void)rb_thread_call_with_gvl(gvl_str_resize, data);
370 memcpy(f->as.value_ptr, val.dptr, val.dsize);
371 f->as.value = f->value;
373 return 0;
376 static void * nogvl_parse_record(void *ptr)
378 struct fetch_parse_args *f = ptr;
380 if (tdb_parse_record(f->tdb, f->as.key, fetch_parse, ptr) == -1)
381 return (void *)Qnil;
383 return (void *)(f->value == f->as.value ? f->value : Qnil);
386 static VALUE fetch(int argc, VALUE *argv, VALUE self)
388 struct fetch_parse_args f;
389 VALUE key;
391 rb_scan_args(argc, argv, "11", &key, &f.value);
392 if (NIL_P(f.value)) {
393 f.value = rb_str_new(0, 0);
394 } else {
395 StringValue(f.value);
396 rb_str_set_len(f.value, 0);
399 f.tdb = db(self, 1);
400 TO_TDB_DATA(f.as.key, key);
402 return (VALUE)my_tbr(nogvl_parse_record, &f);
405 struct store_args {
406 struct tdb_context *tdb;
407 TDB_DATA key;
408 TDB_DATA val;
409 int flag;
412 static void * nogvl_store(void *ptr)
414 struct store_args *s = ptr;
415 long rc = tdb_store(s->tdb, s->key, s->val, s->flag);
417 return (void *)rc;
420 static VALUE rbtdb_store(VALUE self, VALUE key, VALUE val, int flag, int soft)
422 struct store_args s;
424 s.tdb = db(self, 1);
425 TO_TDB_DATA(s.key, key);
426 TO_TDB_DATA(s.val, val);
427 s.flag = flag;
429 if ((long)my_tbr(nogvl_store, &s) == -1) {
430 if (soft) {
431 int ecode = tdb_error(s.tdb);
433 if ((flag == TDB_INSERT) && (ecode == TDB_ERR_EXISTS))
434 return Qnil;
435 if ((flag == TDB_MODIFY) && (ecode == TDB_ERR_NOEXIST))
436 return Qnil;
438 my_raise(s.tdb);
441 return val;
444 static VALUE store(VALUE self, VALUE key, VALUE val)
446 return rbtdb_store(self, key, val, 0, 0);
449 static VALUE insert_bang(VALUE self, VALUE key, VALUE val)
451 return rbtdb_store(self, key, val, TDB_INSERT, 0);
454 static VALUE insert(VALUE self, VALUE key, VALUE val)
456 return rbtdb_store(self, key, val, TDB_INSERT, 1);
459 static VALUE modify_bang(VALUE self, VALUE key, VALUE val)
461 return rbtdb_store(self, key, val, TDB_MODIFY, 0);
464 static VALUE modify(VALUE self, VALUE key, VALUE val)
466 return rbtdb_store(self, key, val, TDB_MODIFY, 1);
469 struct exists_args {
470 struct tdb_context *tdb;
471 TDB_DATA key;
474 static void * nogvl_exists(void *ptr)
476 struct exists_args *e = ptr;
478 return (void *)(tdb_exists(e->tdb, e->key) == 0 ? Qfalse : Qtrue);
481 static VALUE has_key(VALUE self, VALUE key)
483 struct exists_args e;
485 e.tdb = db(self, 1);
486 TO_TDB_DATA(e.key, key);
488 return (VALUE)my_tbr(nogvl_exists, &e);
491 struct traverse_args {
492 struct tdb_context *tdb;
493 TDB_DATA key;
494 TDB_DATA val;
495 int state;
498 static VALUE protected_yield(VALUE val)
500 VALUE *kv = (VALUE *)val;
502 return rb_yield_values(2, kv[0], kv[1]);
505 static void *my_yield(void *data)
507 struct traverse_args *t = data;
508 VALUE kv[2];
510 kv[0] = str_new_tdb_data(&t->key);
511 kv[1] = str_new_tdb_data(&t->val);
513 rb_protect(protected_yield, (VALUE)kv, &t->state);
515 return NULL;
518 static int
519 traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val, void *data)
521 struct traverse_args *t = data;
523 t->key = key;
524 t->val = val;
525 (void)rb_thread_call_with_gvl(my_yield, t);
527 return t->state;
530 static void * nogvl_traverse(void *ptr)
532 struct traverse_args *t = ptr;
534 (void)tdb_traverse(t->tdb, traverse_fn, t);
536 return (void *)Qfalse;
539 static VALUE each(VALUE self)
541 struct traverse_args t;
543 t.tdb = db(self, 1);
544 t.state = 0;
546 my_tbr(nogvl_traverse, &t);
547 if (t.state)
548 rb_jump_tag(t.state);
549 return self;
552 struct delete_args {
553 struct tdb_context *tdb;
554 TDB_DATA key;
557 static void * nogvl_delete(void *ptr)
559 struct delete_args *d = ptr;
561 return (void *)(tdb_delete(d->tdb, d->key) == 0 ? Qtrue : Qfalse);
564 static VALUE nuke(VALUE self, VALUE key)
566 struct delete_args d;
568 d.tdb = db(self, 1);
569 TO_TDB_DATA(d.key, key);
571 return (VALUE)my_tbr(nogvl_delete, &d);
574 static VALUE aref(VALUE self, VALUE key)
576 return fetch(1, &key, self);
579 static VALUE delete(int argc, VALUE *argv, VALUE self)
581 VALUE rc = fetch(argc, argv, self);
583 if (! NIL_P(rc))
584 if (nuke(self, argv[0]) == Qfalse)
585 return Qnil;
586 return rc;
589 static VALUE lockall(VALUE self)
591 struct tdb_context *tdb = db(self, 1);
592 if (my_tbr(tdb_lockall, tdb))
593 my_raise(tdb);
595 return Qtrue;
598 static VALUE trylockall(VALUE self)
600 struct tdb_context *tdb = db(self, 1);
601 void *fn = tdb_lockall_nonblock;
603 if (my_tbr(fn, tdb)) {
604 if (tdb_error(tdb) == TDB_ERR_LOCK)
605 return Qfalse;
606 my_raise(tdb);
608 return Qtrue;
611 static VALUE unlockall(VALUE self)
613 struct tdb_context *tdb = db(self, 1);
614 if (my_tbr(tdb_unlockall, tdb))
615 my_raise(tdb);
616 return Qtrue;
619 static VALUE lockall_read(VALUE self)
621 struct tdb_context *tdb = db(self, 1);
622 if (my_tbr(tdb_lockall_read, tdb))
623 my_raise(tdb);
624 return Qtrue;
627 static VALUE trylockall_read(VALUE self)
629 struct tdb_context *tdb = db(self, 1);
630 void *fn = tdb_lockall_read_nonblock;
631 if (my_tbr(fn, tdb)) {
632 if (tdb_error(tdb) == TDB_ERR_LOCK)
633 return Qfalse;
634 my_raise(tdb);
636 return Qtrue;
639 static VALUE unlockall_read(VALUE self)
641 struct tdb_context *tdb = db(self, 1);
642 if (my_tbr(tdb_unlockall_read, tdb))
643 my_raise(tdb);
644 return Qtrue;
647 static VALUE lockall_mark(VALUE self)
649 struct tdb_context *tdb = db(self, 1);
650 if (my_tbr(tdb_lockall_mark, tdb))
651 my_raise(tdb);
652 return Qtrue;
655 static VALUE lockall_unmark(VALUE self)
657 struct tdb_context *tdb = db(self, 1);
659 if (my_tbr(tdb_lockall_unmark, tdb))
660 my_raise(tdb);
661 return Qtrue;
665 * clears out the database
667 static VALUE clear(VALUE self)
669 struct tdb_context *tdb = db(self, 1);
670 if (my_tbr(tdb_wipe_all, tdb))
671 my_raise(tdb);
672 return self;
675 #ifdef HAVE_TDB_REPACK
676 /* repacks a database to reduce fragmentation, available with tdb 1.2.x+ */
677 static VALUE repack(VALUE self)
679 struct tdb_context *tdb = db(self, 1);
680 if (my_tbr(tdb_repack, tdb))
681 my_raise(tdb);
682 return self;
684 #endif /* HAVE_TDB_REPACK */
686 void Init_tdb_ext(void)
688 cTDB = rb_define_class("TDB", rb_cObject);
690 hashes = rb_hash_new();
693 * Available hash functions, the key is the name of the hash
694 * and the value is a pointer for internal for usage.
696 rb_define_const(cTDB, "HASHES", hashes);
698 rb_define_alloc_func(cTDB, alloc);
699 rb_include_module(cTDB, rb_mEnumerable);
701 rb_define_method(cTDB, "initialize", init, -1);
702 rb_define_method(cTDB, "close", tdbclose, 0);
703 rb_define_method(cTDB, "closed?", closed, 0);
705 rb_define_method(cTDB, "fetch", fetch, -1);
706 rb_define_method(cTDB, "[]", aref, 1);
707 rb_define_method(cTDB, "store", store, 2);
708 rb_define_method(cTDB, "[]=", store, 2);
709 rb_define_method(cTDB, "insert!", insert_bang, 2);
710 rb_define_method(cTDB, "modify!", modify_bang, 2);
711 rb_define_method(cTDB, "insert", insert, 2);
712 rb_define_method(cTDB, "modify", modify, 2);
714 rb_define_method(cTDB, "key?", has_key, 1);
715 rb_define_method(cTDB, "has_key?", has_key, 1);
716 rb_define_method(cTDB, "include?", has_key, 1);
717 rb_define_method(cTDB, "member?", has_key, 1);
718 rb_define_method(cTDB, "each", each, 0);
719 rb_define_method(cTDB, "nuke!", nuke, 1);
720 rb_define_method(cTDB, "delete", delete, -1);
722 rb_define_method(cTDB, "lockall", lockall, 0);
723 rb_define_method(cTDB, "trylockall", trylockall, 0);
724 rb_define_method(cTDB, "unlockall", unlockall, 0);
725 rb_define_method(cTDB, "lockall_read", lockall_read, 0);
726 rb_define_method(cTDB, "trylockall_read", trylockall_read, 0);
727 rb_define_method(cTDB, "unlockall_read", unlockall_read, 0);
728 rb_define_method(cTDB, "lockall_mark", lockall_mark, 0);
729 rb_define_method(cTDB, "lockall_unmark", lockall_unmark, 0);
730 rb_define_method(cTDB, "clear", clear, 0);
731 #ifdef HAVE_TDB_REPACK
732 rb_define_method(cTDB, "repack", repack, 0);
733 #endif /* HAVE_TDB_REPACK */
735 init_errors();
736 init_hashes();
738 /* just a readability place holder */
739 rb_define_const(cTDB, "DEFAULT", UINT2NUM(TDB_DEFAULT));
741 /* clear database if we are the only one with it open */
742 rb_define_const(cTDB, "CLEAR_IF_FIRST", UINT2NUM(TDB_CLEAR_IF_FIRST));
744 /* don't store on disk, use in-memory database */
745 rb_define_const(cTDB, "INTERNAL", UINT2NUM(TDB_INTERNAL));
747 /* don't do any locking */
748 rb_define_const(cTDB, "NOLOCK", UINT2NUM(TDB_NOLOCK));
750 /* don't use mmap */
751 rb_define_const(cTDB, "NOMMAP", UINT2NUM(TDB_NOMMAP));
753 /* convert endian (internal use) */
754 rb_define_const(cTDB, "CONVERT", UINT2NUM(TDB_CONVERT));
756 /* header is big-endian (internal use) */
757 rb_define_const(cTDB, "BIGENDIAN", UINT2NUM(TDB_BIGENDIAN));
759 /* don't use synchronous transactions */
760 rb_define_const(cTDB, "NOSYNC", UINT2NUM(TDB_NOSYNC));
762 /* maintain a sequence number */
763 rb_define_const(cTDB, "SEQNUM", UINT2NUM(TDB_SEQNUM));
765 /* Activate the per-hashchain freelist, default 5 */
766 rb_define_const(cTDB, "VOLATILE", UINT2NUM(TDB_VOLATILE));
768 #ifdef TDB_ALLOW_NESTING
769 /* Allow transactions to nest */
770 rb_define_const(cTDB, "ALLOW_NESTING", UINT2NUM(TDB_ALLOW_NESTING));
771 #endif
773 #ifdef TDB_DISALLOW_NESTING
774 /* Disallow transactions to nest */
775 rb_define_const(cTDB, "DISALLOW_NESTING", UINT2NUM(TDB_DISALLOW_NESTING));
776 #endif
778 #ifdef TDB_INCOMPATIBLE_HASH
779 /* Better hashing, but can't be opened by tdb < 1.2.6. */
780 rb_define_const(cTDB, "INCOMPATIBLE_HASH", UINT2NUM(TDB_INCOMPATIBLE_HASH));
781 #endif
782 rbtdb_init_tdb_hash_functions();
786 * Document-class: TDB
788 * <code>
789 * tdb = TDB.new("/path/to/file", flags => IO::RDWR|IO::CREAT)
790 * tdb.store("HELLO", "world")
791 * tdb.fetch("HELLO") -> "world"
792 * tdb.delete("HELLO") -> "world"
793 * </code>