start from fresh tree
[concurrent.git] / ext / concurrent / futures / futures.c
blobe74f0dc9f8574db69cf77ead8728551b91bd136e
1 /*
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.
7 */
9 #include "ruby.h"
10 #include "rubysig.h"
11 #include "intern.h"
13 static VALUE mConcurrent;
14 static VALUE mFutures;
15 static VALUE eAsyncError;
16 static VALUE cThunk;
18 static ID value_id;
19 static ID inspect_id;
20 static ID respond_to_p_id;
22 typedef struct {
23 VALUE source;
24 VALUE value;
25 } Thunk;
27 static void thunk_copy_atomic(Thunk *to, Thunk const *from) {
28 int saved_critical;
29 saved_critical = rb_thread_critical;
30 rb_thread_critical = 1;
31 *to = *from;
32 rb_thread_critical = saved_critical;
35 static VALUE thunk_value(VALUE obj, int evaluate) {
36 VALUE original;
38 original = obj;
40 while ( CLASS_OF(obj) == cThunk ) {
41 Thunk *thunk;
42 Thunk copy;
44 Data_Get_Struct(obj, Thunk, thunk);
45 thunk_copy_atomic(&copy, thunk);
47 if (RTEST(copy.source)) {
48 if (evaluate) {
49 copy.value = rb_funcall(copy.source, value_id, 0);
50 copy.source = Qnil;
51 thunk_copy_atomic(thunk, &copy);
54 if ( obj != original ) {
55 Thunk *original_thunk;
56 Data_Get_Struct(original, Thunk, original_thunk);
57 thunk_copy_atomic(original_thunk, &copy);
60 if (!evaluate) {
61 break;
65 obj = copy.value;
68 return obj;
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) {
85 free(thunk);
88 static VALUE rb_thunk_new(VALUE klass, VALUE source) {
89 Thunk *thunk;
91 thunk = (Thunk *)malloc(sizeof(Thunk));
93 thunk->source = source;
94 thunk->value = Qnil;
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) {
104 ID name;
106 name = SYM2ID(argv[0]);
107 self = thunk_ground(self);
109 if ( CLASS_OF(self) == cThunk ) {
110 if ( name == inspect_id && argc == 1 ) {
111 Thunk *thunk;
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] )
119 return Qtrue;
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);