ons 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +7 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +4 -0
  7. data/README-zh_CN.md +80 -0
  8. data/README.md +36 -0
  9. data/Rakefile +10 -0
  10. data/bin/console +14 -0
  11. data/bin/rake +9 -0
  12. data/bin/setup +8 -0
  13. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/Action.h +12 -0
  14. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/ConsumeContext.h +15 -0
  15. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/LocalTransactionChecker.h +16 -0
  16. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/LocalTransactionExecuter.h +16 -0
  17. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/Message.h +96 -0
  18. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/MessageListener.h +20 -0
  19. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/ONSChannel.h +17 -0
  20. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/ONSClient.h +28 -0
  21. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/ONSClientException.h +27 -0
  22. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/ONSFactory.h +73 -0
  23. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/Producer.h +29 -0
  24. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/PushConsumer.h +22 -0
  25. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/SendResultONS.h +22 -0
  26. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/TransactionProducer.h +22 -0
  27. data/ext/ons/aliyun-mq-cpp-sdk/include/ons/TransactionStatus.h +15 -0
  28. data/ext/ons/aliyun-mq-cpp-sdk/lib/libonsclient4cpp.so +0 -0
  29. data/ext/ons/consumer.cpp +68 -0
  30. data/ext/ons/consumer.hpp +33 -0
  31. data/ext/ons/extconf.rb +44 -0
  32. data/ext/ons/listener.cpp +19 -0
  33. data/ext/ons/listener.hpp +25 -0
  34. data/ext/ons/lmfao.cpp +251 -0
  35. data/ext/ons/lmfao.hpp +101 -0
  36. data/ext/ons/ons.cpp +19 -0
  37. data/ext/ons/ons.hpp +7 -0
  38. data/ext/ons/producer.cpp +74 -0
  39. data/ext/ons/producer.hpp +30 -0
  40. data/lib/ons/consumer.rb +71 -0
  41. data/lib/ons/lmfao.rb +18 -0
  42. data/lib/ons/producer.rb +80 -0
  43. data/lib/ons/version.rb +3 -0
  44. data/lib/ons.rb +36 -0
  45. data/ons.gemspec +42 -0
  46. data/samples/consumer.rb +32 -0
  47. data/samples/producer.rb +35 -0
  48. metadata +218 -0
@@ -0,0 +1,68 @@
1
+ #include <rice/Constructor.hpp>
2
+ #include <rice/Data_Type.hpp>
3
+ #include <rice/Hash.hpp>
4
+ #include <rice/Module.hpp>
5
+ #include <rice/String.hpp>
6
+ #include <rice/Symbol.hpp>
7
+ #include <ons/ONSFactory.h>
8
+ #include "listener.hpp"
9
+ #include "consumer.hpp"
10
+
11
+ Consumer::Consumer(Rice::String accessKey, Rice::String secretKey, Rice::String consumerId, Rice::Hash options)
12
+ {
13
+ ons::ONSFactoryProperty factoryInfo;
14
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::AccessKey, accessKey.str());
15
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::SecretKey, secretKey.str());
16
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::ConsumerId, consumerId.str());
17
+
18
+ Rice::Object namesrvAddr = options.call("[]", Rice::String("namesrv_addr"));
19
+ if (!namesrvAddr) { namesrvAddr = options.call("[]", Rice::Symbol("namesrv_addr")); }
20
+ if (namesrvAddr) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::NAMESRV_ADDR, ((Rice::String)namesrvAddr).str()); }
21
+
22
+ Rice::Object onsAddr = options.call("[]", Rice::String("ons_addr"));
23
+ if (!onsAddr) { onsAddr = options.call("[]", Rice::Symbol("ons_addr")); }
24
+ if (onsAddr) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::ONSAddr, ((Rice::String)onsAddr).str()); }
25
+
26
+ Rice::Object threadNum = options.call("[]", Rice::String("thread_num"));
27
+ if (!threadNum) { threadNum = options.call("[]", Rice::Symbol("thread_num")); }
28
+ if (threadNum.rb_type() == T_FIXNUM || threadNum.rb_type() == T_BIGNUM) { threadNum = threadNum.to_s(); }
29
+ if (threadNum) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::ConsumeThreadNums, ((Rice::String)threadNum).str()); }
30
+
31
+ this->onsPushConsumer = ons::ONSFactory::getInstance()->createPushConsumer(factoryInfo);
32
+ }
33
+
34
+ Consumer::~Consumer()
35
+ {
36
+ for (std::vector<Listener*>::iterator iter = this->listeners.begin(); iter != this->listeners.end(); ++iter) {
37
+ delete *iter;
38
+ }
39
+ }
40
+
41
+ void Consumer::subscribe(Rice::String topic, Rice::String subscribeExpression, Rice::Object handler)
42
+ {
43
+ Listener *listener = new Listener;
44
+ listener->setHandler(handler);
45
+
46
+ this->listeners.push_back(listener);
47
+ this->onsPushConsumer->subscribe(topic.str(), subscribeExpression.str(), listener);
48
+ }
49
+
50
+ void Consumer::start()
51
+ {
52
+ this->onsPushConsumer->start();
53
+ }
54
+
55
+ void Consumer::shutdown()
56
+ {
57
+ this->onsPushConsumer->shutdown();
58
+ }
59
+
60
+ void define_class_consumer_under_module(Rice::Module module)
61
+ {
62
+ Rice::Data_Type<Consumer> rb_cConsumer = Rice::define_class_under<Consumer>(module, "Consumer");
63
+
64
+ rb_cConsumer.define_constructor(Rice::Constructor<Consumer, Rice::String, Rice::String, Rice::String, Rice::Hash>());
65
+ rb_cConsumer.define_method("subscribe", &Consumer::subscribe);
66
+ rb_cConsumer.define_method("start", &Consumer::start);
67
+ rb_cConsumer.define_method("shutdown", &Consumer::shutdown);
68
+ }
@@ -0,0 +1,33 @@
1
+ #ifndef __RBEXT_CONSUMER_HPP__
2
+ #define __RBEXT_CONSUMER_HPP__
3
+
4
+ #include <vector>
5
+
6
+ namespace Rice { class Hash; class Module; class Object; class String; }
7
+ namespace ons { class PushConsumer; }
8
+ class Listener;
9
+
10
+ class Consumer {
11
+
12
+ public:
13
+ Consumer(Rice::String accessKey, Rice::String secretKey, Rice::String consumerId, Rice::Hash options);
14
+ virtual ~Consumer();
15
+
16
+ virtual void subscribe(Rice::String topic, Rice::String subscribeExpression, Rice::Object handler);
17
+
18
+ // call this method once after subscribe topic.
19
+ virtual void start();
20
+
21
+ // call this method before program exit,
22
+ // otherwise it would cause a memory leak and some other issues.
23
+ virtual void shutdown();
24
+
25
+ private:
26
+ ons::PushConsumer* onsPushConsumer;
27
+ std::vector<Listener*> listeners;
28
+
29
+ };
30
+
31
+ void define_class_consumer_under_module(Rice::Module module);
32
+
33
+ #endif // __RBEXT_CONSUMER_HPP__
@@ -0,0 +1,44 @@
1
+ # rubocop:disable Style/GlobalVars
2
+
3
+ # use mkmf-rice instead mkmf
4
+ require 'mkmf-rice'
5
+
6
+ # aliyun mq sdk dir
7
+ ALIYUN_MQ_CPP_SDK_DIR = File.expand_path('../aliyun-mq-cpp-sdk', __FILE__)
8
+
9
+ # header dirs to search
10
+ HEADER_DIRS = [
11
+ '/opt/local/include', # search /opt/local for macports
12
+ '/usr/local/include', # search /usr/local for people that installed from source
13
+ RbConfig::CONFIG['includedir'], # check the ruby install locations
14
+ File.join(ALIYUN_MQ_CPP_SDK_DIR, 'include'),
15
+ '/usr/include', # finally fall back to /usr
16
+ ].freeze
17
+
18
+ # library dirs to search
19
+ LIB_DIRS = [
20
+ '/opt/local/lib', # search /opt/local for macports
21
+ '/usr/local/lib', # search /usr/local for people that installed from source
22
+ RbConfig::CONFIG['libdir'], # check the ruby install locations
23
+ File.join(ALIYUN_MQ_CPP_SDK_DIR, 'lib'),
24
+ '/usr/lib', # finally fall back to /usr
25
+ ].freeze
26
+
27
+ # configure cxxflags
28
+ $warnflags.gsub!(/-Wdeclaration-after-statement/, '') # this flag is valid for C/ObjC but not for C++
29
+ $warnflags.gsub!(/-Wimplicit-function-declaration/, '') # this flag is valid for C/ObjC but not for C++
30
+ $warnflags << ' -Wno-ignored-qualifiers'
31
+
32
+ # configure various "with" options
33
+ dir_config('boost', HEADER_DIRS, LIB_DIRS)
34
+ dir_config('onsclient4cpp', HEADER_DIRS, LIB_DIRS)
35
+
36
+ # configure link libraries
37
+ abort 'libboost_chrono is missing, please install libboost_chrono' unless have_library('boost_chrono')
38
+ abort 'libboost_system is missing, please install libboost_system' unless have_library('boost_system')
39
+ abort 'libboost_thread is missing, please install libboost_thread' unless have_library('boost_thread')
40
+ abort unless have_library('onsclient4cpp')
41
+ abort unless have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
42
+
43
+ # create Makefile
44
+ create_makefile('ons/ons')
@@ -0,0 +1,19 @@
1
+ #include <rice/Hash.hpp>
2
+ #include "listener.hpp"
3
+ #include "lmfao.hpp"
4
+
5
+ void Listener::setHandler(Rice::Object handler)
6
+ {
7
+ this->handler = handler.value();
8
+ this->riceHandler = handler;
9
+ }
10
+
11
+ Action Listener::consume(ons::Message& message, ons::ConsumeContext& context)
12
+ {
13
+ // Listener::consume will called in a separate thread which is not created by Ruby,
14
+ // so you cannot call any Ruby functions here. Use the LMFAO library workaround.
15
+ bool result = mLMFAO_call(this->handler, &message);
16
+
17
+ if (result) { return CommitMessage; }
18
+ return ReconsumeLater;
19
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef __RBEXT_LISTENER_HPP__
2
+ #define __RBEXT_LISTENER_HPP__
3
+
4
+ #include <rice/Object.hpp>
5
+ #include <ons/MessageListener.h>
6
+
7
+ class Listener: public ons::MessageListener {
8
+
9
+ public:
10
+ Listener() {}
11
+ virtual ~Listener() {}
12
+
13
+ virtual void setHandler(Rice::Object handler);
14
+
15
+ // implement the interface of consuming message.
16
+ virtual Action consume(ons::Message& message, ons::ConsumeContext& context);
17
+
18
+ private:
19
+ // a object which should be repsond to :call method.
20
+ VALUE handler;
21
+ Rice::Object riceHandler;
22
+
23
+ };
24
+
25
+ #endif // __RBEXT_LISTENER_HPP__
data/ext/ons/lmfao.cpp ADDED
@@ -0,0 +1,251 @@
1
+ #include "lmfao.hpp"
2
+
3
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
4
+ * Functions related to the global callback queue
5
+ * * * * * * * * * * * * * * * * * * * * * * * * */
6
+
7
+ /*
8
+ Three globals to allow for Ruby/C-thread communication:
9
+
10
+ - mutex & condition to synchronize access to callback_queue
11
+ - callback_queue to store actual callback data in
12
+
13
+ Be careful with the functions that manipulate the callback
14
+ queue; they must do so in the protection of a mutex.
15
+ */
16
+ pthread_mutex_t g_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
17
+ pthread_cond_t g_callback_cond = PTHREAD_COND_INITIALIZER;
18
+ callback_t *g_callback_queue = NULL;
19
+
20
+ /*
21
+ Use this function to add a callback node onto the global
22
+ callback queue.
23
+
24
+ Do note that we are adding items to the front of the linked
25
+ list, and as such events will always be handled by most recent
26
+ first. To remedy this, add to the end of the queue instead.
27
+ */
28
+ void g_callback_queue_push(callback_t *callback)
29
+ {
30
+ callback->next = g_callback_queue;
31
+ g_callback_queue = callback;
32
+ }
33
+
34
+ /*
35
+ Use this function to pop off a callback node from the
36
+ global callback queue. Returns NULL if queue is empty.
37
+ */
38
+ callback_t *g_callback_queue_pop()
39
+ {
40
+ callback_t *callback = g_callback_queue;
41
+ if (callback)
42
+ {
43
+ g_callback_queue = callback->next;
44
+ }
45
+ return callback;
46
+ }
47
+
48
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
49
+ * Functions related to LMFAO Ruby API
50
+ * * * * * * * * * * * * * * * * * * * * * * * * */
51
+
52
+ /*
53
+ Call LMFAO with the given argument.
54
+ */
55
+ bool mLMFAO_call(VALUE handler, ons::Message* message)
56
+ {
57
+ listener_consume_args_t args = {
58
+ .handler = handler,
59
+ .message = message
60
+ };
61
+ return lmfao_callback((void *) &args);
62
+ }
63
+
64
+ /*
65
+ This is our user-defined C callback, it gets called by the C library.
66
+
67
+ We need to:
68
+
69
+ 1. Create a callback structure, put our parameters in it
70
+ 2. Push the callback node onto the global callback queue
71
+ 3. Wait for the callback to be handled
72
+ 4. Return the return value
73
+ */
74
+ void *lmfao_callback(void *data)
75
+ {
76
+ callback_t callback;
77
+ pthread_mutex_init(&callback.mutex, NULL);
78
+ pthread_cond_init(&callback.cond, NULL);
79
+ callback.data = data;
80
+ callback.handled = false;
81
+
82
+ // Put callback data in global callback queue
83
+ pthread_mutex_lock(&g_callback_mutex);
84
+ g_callback_queue_push(&callback);
85
+ pthread_mutex_unlock(&g_callback_mutex);
86
+
87
+ // Notify waiting Ruby thread that we have callback data
88
+ pthread_cond_signal(&g_callback_cond);
89
+
90
+ // Wait for callback to be handled
91
+ pthread_mutex_lock(&callback.mutex);
92
+ while (callback.handled == false)
93
+ {
94
+ pthread_cond_wait(&callback.cond, &callback.mutex);
95
+ }
96
+ pthread_mutex_unlock(&callback.mutex);
97
+
98
+ // Clean up
99
+ pthread_mutex_destroy(&callback.mutex);
100
+ pthread_cond_destroy(&callback.cond);
101
+
102
+ return callback.data;
103
+ }
104
+
105
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
106
+ * Our special Ruby event-listening thread functions
107
+ * * * * * * * * * * * * * * * * * * * * * * * * */
108
+
109
+ /*
110
+ Executed for each callback notification; what we receive
111
+ are the callback parameters. The job of this method is to:
112
+
113
+ 1. Convert callback parameters into Ruby values
114
+ 2. Call the appropriate callback with said parameters
115
+ 3. Convert the Ruby return value into a C value
116
+ 4. Hand over the C value to the C callback
117
+ */
118
+ VALUE LMFAO_handle_callback(void *cb)
119
+ {
120
+ callback_t *callback = (callback_t*) cb;
121
+
122
+ // figure out the proper handler for this particular event, as
123
+ // well as convert the callback data into proper ruby values!
124
+ listener_consume_args_t *args = (listener_consume_args_t*) callback->data;
125
+ ons::Message *message = args->message;
126
+ VALUE proc = args->handler;
127
+ VALUE hash = rb_hash_new();
128
+ rb_hash_aset(hash, ID2SYM(rb_intern("id")), rb_str_new2(message->getMsgID().c_str()));
129
+ rb_hash_aset(hash, ID2SYM(rb_intern("topic")), rb_str_new2(message->getTopic().c_str()));
130
+ rb_hash_aset(hash, ID2SYM(rb_intern("tag")), rb_str_new2(message->getTag().c_str()));
131
+ rb_hash_aset(hash, ID2SYM(rb_intern("body")), rb_str_new2(message->getBody().c_str()));
132
+ rb_hash_aset(hash, ID2SYM(rb_intern("key")), rb_str_new2(message->getKey().c_str()));
133
+ rb_hash_aset(hash, ID2SYM(rb_intern("reconsume_times")), INT2NUM(message->getReconsumeTimes()));
134
+ rb_hash_aset(hash, ID2SYM(rb_intern("store_timestamp")), LL2NUM(message->getStoreTimestamp()));
135
+ rb_hash_aset(hash, ID2SYM(rb_intern("deliver_timestamp")), LL2NUM(message->getStartDeliverTime()));
136
+ VALUE result = rb_funcall(proc, rb_intern("call"), 1, hash);
137
+
138
+ // convert it to a C value that our callback can return
139
+ if (result == Qnil || result == Qfalse) {
140
+ callback->data = (void *) false;
141
+ } else {
142
+ callback->data = (void *) true;
143
+ }
144
+
145
+ // tell the callback that it has been handled, we are done
146
+ pthread_mutex_lock(&callback->mutex);
147
+ callback->handled = true;
148
+ pthread_cond_signal(&callback->cond);
149
+ pthread_mutex_unlock(&callback->mutex);
150
+
151
+ return Qnil;
152
+ }
153
+
154
+ /*
155
+ Wait for global callback queue to contain something.
156
+
157
+ This function is called while not holding the GVL, so it’s safe
158
+ to do long waits in here; other threads will still run.
159
+
160
+ The job of this function is merely to wait until the callback queue
161
+ contains something. Once that happens, we remove the item from the
162
+ queue and return it to our caller through waiting->callback.
163
+ */
164
+ void *wait_for_callback_signal(void *w)
165
+ {
166
+ callback_waiting_t *waiting = (callback_waiting_t*) w;
167
+
168
+ pthread_mutex_lock(&g_callback_mutex);
169
+
170
+ // abort signal is used when ruby wants us to stop waiting
171
+ while (waiting->abort == false && (waiting->callback = g_callback_queue_pop()) == NULL)
172
+ {
173
+ pthread_cond_wait(&g_callback_cond, &g_callback_mutex);
174
+ }
175
+
176
+ pthread_mutex_unlock(&g_callback_mutex);
177
+
178
+ return NULL;
179
+ }
180
+
181
+ /*
182
+ Stop waiting for callback notification. This function
183
+ is invoked by Ruby when she wants us to exit.
184
+
185
+ As `wait_for_callback_signal` function is executed without holding
186
+ the GVL, it can potentially take forever. However, when somebody wants
187
+ to quit the program (for example if somebody presses CTRL-C to exit)
188
+ we must tell Ruby how to wake up the `wait_for_callback_signal` function
189
+ so Ruby can exit properly.
190
+ */
191
+ void stop_waiting_for_callback_signal(void *w)
192
+ {
193
+ callback_waiting_t *waiting = (callback_waiting_t*) w;
194
+
195
+ pthread_mutex_lock(&g_callback_mutex);
196
+ waiting->abort = true;
197
+ pthread_cond_signal(&g_callback_cond);
198
+ pthread_mutex_unlock(&g_callback_mutex);
199
+ }
200
+
201
+ /*
202
+ This thread loops continously, waiting for callbacks to happen in C.
203
+ Once they do, it will handle the callback in a new thread.
204
+
205
+ The reason we use a thread for handling the callback is that the handler
206
+ itself might fire off more callbacks, that might need to be handled before
207
+ the handler returns. We can’t do that if the event thread is busy handling
208
+ the first callback.
209
+
210
+ Continously, we need to:
211
+
212
+ 1. Unlock the Ruby GVL (allowing other threads to run while we wait)
213
+ 2. Wait for a callback to fire
214
+ 3. Spawn a new ruby thread to handle the callback
215
+ */
216
+ VALUE LMFAO_event_thread(void *unused)
217
+ {
218
+ callback_waiting_t waiting = {
219
+ .callback = NULL,
220
+ .abort = false
221
+ };
222
+
223
+ while (waiting.abort == false)
224
+ {
225
+ // release the GVL while waiting for a callback notification
226
+ rb_thread_call_without_gvl(wait_for_callback_signal, &waiting, stop_waiting_for_callback_signal, &waiting);
227
+
228
+ // if ruby wants us to abort, this will be NULL
229
+ if (waiting.callback)
230
+ {
231
+ rb_thread_create((VALUE (*)(...)) LMFAO_handle_callback, (void *) waiting.callback);
232
+ }
233
+ }
234
+
235
+ return Qnil;
236
+ }
237
+
238
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
239
+ * Ruby bindings for LMFAO C library
240
+ * * * * * * * * * * * * * * * * * * * * * * * * */
241
+
242
+ Rice::Object start_event_thread()
243
+ {
244
+ return rb_thread_create((VALUE (*)(...)) LMFAO_event_thread, NULL);
245
+ }
246
+
247
+ void define_module_lmfao_under_module(Rice::Module module)
248
+ {
249
+ Rice::Module rb_mLMFAO = module.define_module("LMFAO");
250
+ rb_mLMFAO.define_singleton_method("start_event_thread", &start_event_thread);
251
+ }
data/ext/ons/lmfao.hpp ADDED
@@ -0,0 +1,101 @@
1
+ #ifndef __RBEXT_LMFAO_HPP__
2
+ #define __RBEXT_LMFAO_HPP__
3
+
4
+ /*
5
+ Problem:
6
+
7
+ You cannot call Ruby functions from a thread not created by Ruby. When it comes to MRI, you
8
+ must be the owner of the GVL to call ruby functions, and since the thread is not a
9
+ ruby-created thread you have no way of owning the GVL.
10
+
11
+ Workaround:
12
+
13
+ 1. we have a special ruby thread, waiting to be notified
14
+ 2. when C callback is invoked, it stores its’ parameters somewhere, notifies ruby thread and waits
15
+ 3. ruby thread is notified, reads the callback parameters, and executes the callback handler
16
+ 4. ruby thread puts the return value of the handler in location where C callback can reach it, and notifies C callback
17
+ 5. C callback wakes up again, reads return value and returns it
18
+
19
+ Reference:
20
+
21
+ * http://www.ruby-forum.com/topic/3149838
22
+ * http://www.burgestrand.se/articles/asynchronous-callbacks-in-ruby-c-extensions
23
+ */
24
+
25
+ #include <rice/Module.hpp>
26
+ #include <ons/Message.h>
27
+ #include <ruby/thread.h>
28
+ #include <pthread.h>
29
+
30
+ typedef struct listener_consume_args_t listener_consume_args_t;
31
+ struct listener_consume_args_t {
32
+ VALUE handler;
33
+ ons::Message *message;
34
+ };
35
+
36
+ typedef struct callback_t callback_t;
37
+ struct callback_t {
38
+ /*
39
+ Each callback needs to store its’ data somewhere; this
40
+ is how we later on access that data from our Ruby thread.
41
+
42
+ We also use this for our return value from the Ruby handler.
43
+ */
44
+ void *data;
45
+
46
+ /*
47
+ Once we’ve dispatched our callback data to Ruby, we must
48
+ wait for a reply before we continue. These two are used
49
+ for that purpose.
50
+ */
51
+ pthread_mutex_t mutex;
52
+ pthread_cond_t cond;
53
+
54
+ /*
55
+ Even though we use the condition variable above to wait,
56
+ we might still be woken up (spurious wakeups). This bool
57
+ serves as a final check that tells us if we can continue.
58
+ */
59
+ bool handled;
60
+
61
+ /*
62
+ We use this to implement a linked list of callback data.
63
+ This allows multiple callbacks to happen simultaneously
64
+ without them having to wait for each other.
65
+ */
66
+ callback_t *next;
67
+ };
68
+
69
+ typedef struct callback_waiting_t callback_waiting_t;
70
+ struct callback_waiting_t {
71
+ callback_t *callback;
72
+ bool abort;
73
+ };
74
+
75
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
76
+ * Functions related to the global callback queue
77
+ * * * * * * * * * * * * * * * * * * * * * * * * */
78
+ void g_callback_queue_push(callback_t *callback);
79
+ callback_t *g_callback_queue_pop();
80
+
81
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
82
+ * Functions related to LMFAO Ruby API
83
+ * * * * * * * * * * * * * * * * * * * * * * * * */
84
+ bool mLMFAO_call(VALUE handler, ons::Message* message);
85
+ void *lmfao_callback(void *data);
86
+
87
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
88
+ * Our special Ruby event-listening thread functions
89
+ * * * * * * * * * * * * * * * * * * * * * * * * */
90
+ VALUE LMFAO_handle_callback(void *cb);
91
+ void *wait_for_callback_signal(void *w);
92
+ void stop_waiting_for_callback_signal(void *w);
93
+ VALUE LMFAO_event_thread(void *unused);
94
+
95
+ /* * * * * * * * * * * * * * * * * * * * * * * * *
96
+ * Ruby bindings for LMFAO C library
97
+ * * * * * * * * * * * * * * * * * * * * * * * * */
98
+ Rice::Object start_event_thread();
99
+ void define_module_lmfao_under_module(Rice::Module module);
100
+
101
+ #endif // __RBEXT_LMFAO_HPP__
data/ext/ons/ons.cpp ADDED
@@ -0,0 +1,19 @@
1
+ #include <rice/Module.hpp>
2
+ #include "lmfao.hpp"
3
+ #include "consumer.hpp"
4
+ #include "producer.hpp"
5
+ #include "ons.hpp"
6
+
7
+ extern "C"
8
+ void Init_ons()
9
+ {
10
+ RUBY_TRY
11
+ {
12
+ Rice::Module rb_mOns = Rice::define_module("Ons");
13
+ Rice::Module rb_mInternal = rb_mOns.define_module("Internal");
14
+ define_class_consumer_under_module(rb_mInternal);
15
+ define_class_producer_under_module(rb_mInternal);
16
+ define_module_lmfao_under_module(rb_mInternal);
17
+ }
18
+ RUBY_CATCH
19
+ }
data/ext/ons/ons.hpp ADDED
@@ -0,0 +1,7 @@
1
+ #ifndef __RBEXT_ONS_HPP__
2
+ #define __RBEXT_ONS_HPP__
3
+
4
+ extern "C"
5
+ void Init_ons();
6
+
7
+ #endif // __RBEXT_ONS_HPP__
@@ -0,0 +1,74 @@
1
+ #include <rice/Constructor.hpp>
2
+ #include <rice/Data_Type.hpp>
3
+ #include <rice/Hash.hpp>
4
+ #include <rice/Module.hpp>
5
+ #include <rice/Object.hpp>
6
+ #include <rice/String.hpp>
7
+ #include <rice/Symbol.hpp>
8
+ #include <ons/ONSFactory.h>
9
+ #include "producer.hpp"
10
+
11
+ Producer::Producer(Rice::String accessKey, Rice::String secretKey, Rice::String producerId, Rice::Hash options)
12
+ {
13
+ ons::ONSFactoryProperty factoryInfo;
14
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::AccessKey, accessKey.str());
15
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::SecretKey, secretKey.str());
16
+ factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::ProducerId, producerId.str());
17
+
18
+ Rice::Object namesrvAddr = options.call("[]", Rice::String("namesrv_addr"));
19
+ if (!namesrvAddr) { namesrvAddr = options.call("[]", Rice::Symbol("namesrv_addr")); }
20
+ if (namesrvAddr) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::NAMESRV_ADDR, ((Rice::String)namesrvAddr).str()); }
21
+
22
+ Rice::Object onsAddr = options.call("[]", Rice::String("ons_addr"));
23
+ if (!onsAddr) { onsAddr = options.call("[]", Rice::Symbol("ons_addr")); }
24
+ if (onsAddr) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::ONSAddr, ((Rice::String)onsAddr).str()); }
25
+
26
+ Rice::Object sendTimeout = options.call("[]", Rice::String("send_timeout"));
27
+ if (!sendTimeout) { sendTimeout = options.call("[]", Rice::Symbol("send_timeout")); }
28
+ if (sendTimeout.rb_type() == T_FIXNUM || sendTimeout.rb_type() == T_BIGNUM) { sendTimeout = sendTimeout.to_s(); }
29
+ if (sendTimeout) { factoryInfo.setFactoryProperty(ons::ONSFactoryProperty::SendMsgTimeoutMillis, ((Rice::String)sendTimeout).str()); }
30
+
31
+ this->onsProducer = ons::ONSFactory::getInstance()->createProducer(factoryInfo);
32
+ }
33
+
34
+ Rice::String Producer::sendMessage(Rice::String topic, Rice::String tag, Rice::String body, Rice::String key)
35
+ {
36
+ ons::Message message(topic.str(), tag.str(), body.str());
37
+ if (key.length() != 0) { message.setKey(key.str()); }
38
+
39
+ ons::SendResultONS sendResult = this->onsProducer->send(message);
40
+ return sendResult.getMessageId();
41
+ }
42
+
43
+ Rice::String Producer::sendTimerMessage(Rice::String topic, Rice::String tag, Rice::String body, Rice::Object deliverTimestamp, Rice::String key)
44
+ {
45
+ ons::Message message(topic.str(), tag.str(), body.str());
46
+ if (key.length() != 0) { message.setKey(key.str()); }
47
+
48
+ long long timestamp = NUM2LL(deliverTimestamp.value());
49
+ if (timestamp != 0) { message.setStartDeliverTime(timestamp); }
50
+
51
+ ons::SendResultONS sendResult = this->onsProducer->send(message);
52
+ return sendResult.getMessageId();
53
+ }
54
+
55
+ void Producer::start()
56
+ {
57
+ this->onsProducer->start();
58
+ }
59
+
60
+ void Producer::shutdown()
61
+ {
62
+ this->onsProducer->shutdown();
63
+ }
64
+
65
+ void define_class_producer_under_module(Rice::Module module)
66
+ {
67
+ Rice::Data_Type<Producer> rb_cProducer = Rice::define_class_under<Producer>(module, "Producer");
68
+
69
+ rb_cProducer.define_constructor(Rice::Constructor<Producer, Rice::String, Rice::String, Rice::String, Rice::Hash>());
70
+ rb_cProducer.define_method("send_message", &Producer::sendMessage);
71
+ rb_cProducer.define_method("send_timer_message", &Producer::sendTimerMessage);
72
+ rb_cProducer.define_method("start", &Producer::start);
73
+ rb_cProducer.define_method("shutdown", &Producer::shutdown);
74
+ }
@@ -0,0 +1,30 @@
1
+ #ifndef __RBEXT_PRODUCER_HPP__
2
+ #define __RBEXT_PRODUCER_HPP__
3
+
4
+ namespace Rice { class Hash; class Module; class Object; class String; }
5
+ namespace ons { class Producer; }
6
+
7
+ class Producer {
8
+
9
+ public:
10
+ Producer(Rice::String accessKey, Rice::String secretKey, Rice::String producerId, Rice::Hash options);
11
+ virtual ~Producer() {}
12
+
13
+ virtual Rice::String sendMessage(Rice::String topic, Rice::String tag, Rice::String body, Rice::String key);
14
+ virtual Rice::String sendTimerMessage(Rice::String topic, Rice::String tag, Rice::String body, Rice::Object deliverTimestamp, Rice::String key);
15
+
16
+ // call this method once before send any messages.
17
+ virtual void start();
18
+
19
+ // call this method before program exit,
20
+ // otherwise it would cause a memory leak and some other issues.
21
+ virtual void shutdown();
22
+
23
+ private:
24
+ ons::Producer* onsProducer;
25
+
26
+ };
27
+
28
+ void define_class_producer_under_module(Rice::Module module);
29
+
30
+ #endif // __RBEXT_PRODUCER_HPP__