ons 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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__