2 * concurrent/futures - futures and lazy evaluation for Ruby
4 * Copyright (C) 2007 MenTaLguY <mental@rydia.net>
6 * This file is made available under the same terms as Ruby.
13 static VALUE mConcurrent
;
14 static VALUE mFutures
;
15 static VALUE eAsyncError
;
20 static ID respond_to_p_id
;
27 static void thunk_copy_atomic(Thunk
*to
, Thunk
const *from
) {
29 saved_critical
= rb_thread_critical
;
30 rb_thread_critical
= 1;
32 rb_thread_critical
= saved_critical
;
35 static VALUE
thunk_value(VALUE obj
, int evaluate
) {
40 while ( CLASS_OF(obj
) == cThunk
) {
44 Data_Get_Struct(obj
, Thunk
, thunk
);
45 thunk_copy_atomic(©
, thunk
);
47 if (RTEST(copy
.source
)) {
49 copy
.value
= rb_funcall(copy
.source
, value_id
, 0);
51 thunk_copy_atomic(thunk
, ©
);
54 if ( obj
!= original
) {
55 Thunk
*original_thunk
;
56 Data_Get_Struct(original
, Thunk
, original_thunk
);
57 thunk_copy_atomic(original_thunk
, ©
);
71 static VALUE
thunk_eval(VALUE thunk
) {
72 return thunk_value(thunk
, 1);
75 static VALUE
thunk_ground(VALUE thunk
) {
76 return thunk_value(thunk
, 0);
79 void thunk_mark(Thunk
const *thunk
) {
80 rb_gc_mark(thunk
->source
);
81 rb_gc_mark(thunk
->value
);
84 void thunk_free(Thunk
*thunk
) {
88 static VALUE
rb_thunk_new(VALUE klass
, VALUE source
) {
91 thunk
= (Thunk
*)malloc(sizeof(Thunk
));
93 thunk
->source
= source
;
96 return Data_Wrap_Struct(cThunk
, thunk_mark
, thunk_free
, thunk
);
99 static VALUE
wrap_exception(VALUE unused
, VALUE ex
) {
100 rb_exc_raise(rb_funcall(eAsyncError
, rb_intern("new"), 1, ex
));
103 static VALUE
rb_thunk_method_missing(int argc
, VALUE
*argv
, VALUE self
) {
106 name
= SYM2ID(argv
[0]);
107 self
= thunk_ground(self
);
109 if ( CLASS_OF(self
) == cThunk
) {
110 if ( name
== inspect_id
&& argc
== 1 ) {
112 Data_Get_Struct(self
, Thunk
, thunk
);
113 /* FIXME: thunk->source might be nil by the time we get here */
114 return rb_str_plus(rb_str_plus(rb_str_new2("#<Thunk "), rb_funcall(thunk
->source
, inspect_id
, 0)), rb_str_new2(">"));
115 } else if ( name
== respond_to_p_id
&& argc
== 2 ) {
116 if ( ID2SYM(inspect_id
) == argv
[1] ||
117 ID2SYM(respond_to_p_id
) == argv
[1] )
124 self
= rb_rescue2(thunk_eval
, self
, wrap_exception
, Qnil
, rb_cObject
, 0);
126 return rb_funcall3(self
, name
, argc
-1, argv
+1);
129 static VALUE
rb_thunk_value(VALUE self
, VALUE thunk_v
) {
130 return thunk_eval(thunk_v
);
133 void Init_futures() {
134 value_id
= rb_intern("value");
135 inspect_id
= rb_intern("inspect");
136 respond_to_p_id
= rb_intern("respond_to?");
138 mConcurrent
= rb_define_module("Concurrent");
139 mFutures
= rb_define_module_under(mConcurrent
, "Futures");
141 eAsyncError
= rb_define_class_under(mFutures
, "AsyncError", rb_eRuntimeError
);
143 cThunk
= rb_class_boot(0); /* not Qnil */
144 rb_singleton_class(cThunk
);
145 rb_undef_alloc_func(cThunk
);
146 rb_const_set(mFutures
, rb_intern("Thunk"), cThunk
);
148 rb_define_singleton_method(cThunk
, "new", rb_thunk_new
, 1);
149 rb_define_singleton_method(cThunk
, "value", rb_thunk_value
, 1);
150 rb_define_private_method(cThunk
, "method_missing",
151 rb_thunk_method_missing
, -1);