mosquitto 0.2 → 0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92a30539c75dec74a32576327cc88bd883502e89
4
- data.tar.gz: 7d44ae4edbd3c241ba38048d1ae46ec1e2948194
3
+ metadata.gz: 9b3853298fbced225fd9fe2c425183e4f95ed036
4
+ data.tar.gz: 929f570d1f74476e6c21baffbe4c12aedc1d48c0
5
5
  SHA512:
6
- metadata.gz: 99410f286d02e75c29f7663522c8b8a9f05bb35ccb7fce3f0e9ed8b6ea95b560ec2bd217dfc4c576db50bc38125a63d686cc4bcc3caf56ee77951dbe1e7a22d1
7
- data.tar.gz: 49240900ce920a90cb5d5be4a7a5b9add7d576b1edf236b8623b0d95b4c9fb54879a86f20821ead46ba6d62f11674aa14ec9c1fb3ee307742781a84b120021e7
6
+ metadata.gz: 32319113ea684f52dab351e4dbd00c0c93bf8c610e1eb8b8cc2a22cc9e610ab377401a4e77330d046b7749398ca958543ebc71128b6a137e456fcd4905c5e996
7
+ data.tar.gz: a64bde7305272ce12d885b60c182de90f61387911f413fedf4ad532df72dde881161fa6b464e0c9bb08bcb8a68cc8b6491f290d7f71be0f72a1a0fa5b67acb60
data/.gitignore CHANGED
@@ -18,4 +18,5 @@ doc/*
18
18
  pkg
19
19
  scratch
20
20
  ext/mosquitto/Makefile
21
- .yardoc
21
+ .yardoc
22
+ test/mosquitto.conf
@@ -1,14 +1,17 @@
1
1
  language: ruby
2
2
  before_install:
3
3
  - sudo apt-get update -qq
4
- - sudo apt-get install pkg-config cmake openssl
5
- - wget http://mosquitto.org/files/source/mosquitto-1.2.3.tar.gz
4
+ - sudo apt-get install pkg-config cmake openssl libc-ares-dev
5
+ - wget http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz
6
6
  install:
7
- - tar xzf mosquitto-1.2.3.tar.gz
8
- - cd mosquitto-1.2.3
7
+ - tar xzf mosquitto-1.3.1.tar.gz
8
+ - cd mosquitto-1.3.1
9
9
  - cmake .
10
10
  - sudo make install
11
11
  - bundle install
12
+ before_script:
13
+ - erb $TRAVIS_BUILD_DIR/test/mosquitto.conf.erb > $TRAVIS_BUILD_DIR/test/mosquitto.conf
14
+ - mosquitto -d -c $TRAVIS_BUILD_DIR/test/mosquitto.conf
12
15
  bundler_args: --quiet
13
16
  rvm:
14
17
  - 1.9.3
@@ -18,12 +21,12 @@ rvm:
18
21
  env:
19
22
  - GC_STRESS=0
20
23
  - GC_STRESS=1
21
- script: "bundle exec rake"
24
+ script:
25
+ - bundle exec rake test:unit
26
+ - bundle exec rake test:integration
22
27
  gemfile:
23
28
  - Gemfile
24
29
  notifications:
25
30
  recipients:
26
31
  - lourens@methodmissing.com
27
- branches:
28
- only:
29
- - master
32
+ - errordeveloper@gmail.com
data/Gemfile CHANGED
@@ -1,6 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
4
-
5
- gem 'rake'
6
- gem 'rdoc'
3
+ gemspec
@@ -1,25 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mosquitto (0.1)
4
+ mosquitto (0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- json (1.8.1)
10
- rake (10.1.1)
9
+ rake (10.2.2)
11
10
  rake-compiler (0.9.2)
12
11
  rake
13
- rdoc (4.1.1)
14
- json (~> 1.4)
15
- yard (0.8.7.3)
12
+ yard (0.8.7.4)
16
13
 
17
14
  PLATFORMS
18
15
  ruby
19
16
 
20
17
  DEPENDENCIES
21
18
  mosquitto!
22
- rake
23
19
  rake-compiler (~> 0.9.2)
24
- rdoc
25
20
  yard
data/README.md CHANGED
@@ -6,9 +6,9 @@ The mosquitto gem is meant to serve as an easy, performant and standards complia
6
6
 
7
7
  The API consists of two classes:
8
8
 
9
- [Mosquitto::Client](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client) - the client
9
+ [Mosquitto::Client](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client) - the client
10
10
 
11
- [Mosquitto::Message](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Message) - returned from the client
11
+ [Mosquitto::Message](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Message) - returned from the client
12
12
 
13
13
  ## About MQTT and libmosquitto
14
14
 
@@ -37,7 +37,7 @@ See the [project website](http://mosquitto.org/) for more information.
37
37
 
38
38
  ## Requirements
39
39
 
40
- This gem links against version 1.2.3 of `libmosquitto` . You will need to install additional packages for your system.
40
+ This gem links against version 1.3.1 of `libmosquitto` . You will need to install additional packages for your system.
41
41
 
42
42
  ### OS X
43
43
 
@@ -51,17 +51,17 @@ brew install mosquitto
51
51
 
52
52
  ``` sh
53
53
  sudo apt-get update
54
- sudo apt-get install pkg-config cmake openssl
54
+ sudo apt-get install pkg-config cmake openssl libc-ares-dev
55
55
  ```
56
56
 
57
- The current Ubuntu packages aren't on 1.2.3 yet - it's recommended to build libmosquitto from source (see below) until further notice. OpenSSL is an optional dependency - libmosquitto builds without it, however TLS specific features would not be available.
57
+ The current Ubuntu packages aren't on 1.3.1 yet - it's recommended to build libmosquitto from source (see below) until further notice. OpenSSL is an optional dependency - libmosquitto builds without it, however TLS specific features would not be available.
58
58
 
59
59
  ### Building libmosquitto from source
60
60
 
61
61
  ``` sh
62
- wget http://mosquitto.org/files/source/mosquitto-1.2.3.tar.gz
63
- tar xzf mosquitto-1.2.3.tar.gz
64
- cd mosquitto-1.2.3
62
+ wget http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz
63
+ tar xzf mosquitto-1.3.1.tar.gz
64
+ cd mosquitto-1.3.1
65
65
  cmake .
66
66
  sudo make install
67
67
  ```
@@ -87,7 +87,7 @@ When are requirements or dependencies are met, the following should install mosq
87
87
  gem install mosquitto
88
88
  ```
89
89
 
90
- The extension checks for libmosquitto presence as well as a 1.2.3 version.
90
+ The extension checks for libmosquitto presence as well as a 1.3.1 version.
91
91
 
92
92
  ## Usage
93
93
 
@@ -180,17 +180,17 @@ publisher.connect("test.mosquitto.org", 1883, 10)
180
180
 
181
181
  The following callbacks are supported (please follow links for further documentation) :
182
182
 
183
- * [connect](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_connect) - called when the broker sends a CONNACK message in response to a connection.
184
- * [disconnect](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_disconnect) - called when the broker has received the DISCONNECT command and has disconnected the client.
185
- * [log](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_log) - should be used if you want event logging information from the client library.
186
- * [subscribe](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_subscribe) - called when the broker responds to a subscription request.
187
- * [unsubscribe](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_unsubscribe) - called when the broker responds to a unsubscription request.
188
- * [publish](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_publish) - called when a message initiated with Mosquitto::Client#publish has been sent to the broker successfully.
189
- * [message](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:on_message) - called when a message is received from the broker.
183
+ * [connect](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_connect) - called when the broker sends a CONNACK message in response to a connection.
184
+ * [disconnect](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_disconnect) - called when the broker has received the DISCONNECT command and has disconnected the client.
185
+ * [log](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_log) - should be used if you want event logging information from the client library.
186
+ * [subscribe](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_subscribe) - called when the broker responds to a subscription request.
187
+ * [unsubscribe](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_unsubscribe) - called when the broker responds to a unsubscription request.
188
+ * [publish](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_publish) - called when a message initiated with Mosquitto::Client#publish has been sent to the broker successfully.
189
+ * [message](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:on_message) - called when a message is received from the broker.
190
190
 
191
191
  ### TLS / SSL
192
192
 
193
- libmosquitto builds with TLS support by default, however [pre-shared key (PSK)](http://rubydoc.info/github/bear-metal/mosquitto/master/Mosquitto/Client:tls_psk_set) support is not available when linked against older OpenSSL versions.
193
+ libmosquitto builds with TLS support by default, however [pre-shared key (PSK)](http://rubydoc.info/github/xively/mosquitto/master/Mosquitto/Client:tls_psk_set) support is not available when linked against older OpenSSL versions.
194
194
 
195
195
  ``` ruby
196
196
  tls_client = Mosquitto::Client.new
@@ -207,13 +207,13 @@ tls_client.tls_set('/path/to/mosquitto.org.crt'), nil, nil, nil, nil)
207
207
  tls_client.connect('test.mosquitto.org', 8883, 10)
208
208
  ```
209
209
 
210
- See [documentation](http://rubydoc.info/github/bear-metal/mosquitto) for the full API specification.
210
+ See [documentation](http://rubydoc.info/github/xively/mosquitto) for the full API specification.
211
211
 
212
212
  ## Contact, feedback and bugs
213
213
 
214
214
  This extension is currently maintained by Lourens Naudé (http://github.com/methodmissing) and contributors.
215
215
 
216
- Please file bugs / issues and feature requests on the [issue tracker](https://github.com/bear-metal/mosquitto/issues)
216
+ Please file bugs / issues and feature requests on the [issue tracker](https://github.com/xively/mosquitto/issues)
217
217
 
218
218
  ## Development
219
219
 
@@ -222,9 +222,9 @@ Use 'bundle install' to install the necessary development and testing gems:
222
222
 
223
223
  ``` sh
224
224
  bundle install
225
- rake
225
+ bundle exec rake
226
226
  ```
227
- Tests by default run against the public `test.mosquitto.org` MQTT server, which supports TLS as well. More information is available at http://test.mosquitto.org/. Alternatively, should you wish you run tests against a local MQTT broker, change the following constants in the test helper to `localhost`:
227
+ Tests by default run against an MQTT server on localhost, which is expected to support TLS on port 8883 as well.
228
228
 
229
229
  ``` ruby
230
230
  class MosquittoTestCase < Test::Unit::TestCase
@@ -237,7 +237,7 @@ class MosquittoTestCase < Test::Unit::TestCase
237
237
 
238
238
  ## Resources
239
239
 
240
- Documentation available at http://rubydoc.info/github/bear-metal/mosquitto
240
+ Documentation available at http://rubydoc.info/github/xively/mosquitto
241
241
 
242
242
  ## Special Thanks
243
243
 
data/Rakefile CHANGED
@@ -17,18 +17,20 @@ Rake::ExtensionTask.new('mosquitto') do |ext|
17
17
  CLEAN.include 'lib/**/mosquitto_ext.*'
18
18
  end
19
19
 
20
- desc 'Run mosquitto tests'
21
- Rake::TestTask.new(:test) do |t|
22
- t.test_files = Dir.glob("test/**/test_*.rb")
23
- t.verbose = true
24
- t.warning = true
25
- end
20
+ namespace :test do
21
+ desc 'Run mosquitto tests'
22
+ Rake::TestTask.new(:unit) do |t|
23
+ t.test_files = Dir.glob("test/**/test_*.rb").reject{|t| t =~ /integration/ }
24
+ t.verbose = true
25
+ t.warning = true
26
+ end
26
27
 
27
- desc 'Run mosquitto integration tests'
28
- Rake::TestTask.new(:integration) do |t|
29
- t.test_files = Dir.glob("test/test_integration.rb")
30
- t.verbose = true
31
- t.warning = true
28
+ desc 'Run mosquitto integration tests'
29
+ Rake::TestTask.new(:integration) do |t|
30
+ t.test_files = Dir.glob("test/test_integration.rb")
31
+ t.verbose = true
32
+ t.warning = true
33
+ end
32
34
  end
33
35
 
34
36
  namespace :debug do
@@ -38,5 +40,7 @@ namespace :debug do
38
40
  end
39
41
  end
40
42
 
41
- task :test => :compile
43
+ task 'test:unit' => :compile
44
+ task 'test:integration' => :compile
45
+ task :test => ['test:unit', 'test:integration']
42
46
  task :default => :test
@@ -1,14 +1,16 @@
1
1
  #include "mosquitto_ext.h"
2
2
 
3
3
  static void rb_mosquitto_run_callback(mosquitto_callback_t *callback);
4
+ static void rb_mosquitto_free_callback(mosquitto_callback_t *callback);
5
+ static void rb_mosquitto_client_reap_event_thread(mosquitto_client_wrapper *client);
4
6
 
5
7
  VALUE mosquitto_tls_password;
6
8
 
7
9
  static int rb_mosquitto_tls_password_callback(char *buf, int size, int rwflag, void *obj)
8
10
  {
9
11
  strncpy(buf, StringValueCStr(mosquitto_tls_password), size);
10
- return RSTRING_LEN(mosquitto_tls_password);
11
12
  rb_gc_unregister_address(&mosquitto_tls_password);
13
+ return RSTRING_LEN(mosquitto_tls_password);
12
14
  }
13
15
 
14
16
  /*
@@ -47,10 +49,10 @@ static mosquitto_callback_t *mosquitto_callback_queue_pop(mosquitto_client_wrapp
47
49
  * Only applicable to clients that run with the threaded Mosquitto::Client#loop_start event loop
48
50
  *
49
51
  */
50
- static void *mosquitto_wait_for_callbacks(void *w)
52
+ static void *mosquitto_wait_for_callbacks(void *c)
51
53
  {
52
- mosquitto_callback_waiting_t *waiter = (mosquitto_callback_waiting_t *)w;
53
- mosquitto_client_wrapper *client = waiter->client;
54
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)c;
55
+ mosquitto_callback_waiting_t *waiter = client->waiter;
54
56
 
55
57
  pthread_mutex_lock(&client->callback_mutex);
56
58
  while (!waiter->abort && (waiter->callback = mosquitto_callback_queue_pop(client)) == NULL)
@@ -69,13 +71,17 @@ static void *mosquitto_wait_for_callbacks(void *w)
69
71
  * Only applicable to clients that run with the threaded Mosquitto::Client#loop_start event loop
70
72
  *
71
73
  */
72
- static void mosquitto_stop_waiting_for_callbacks(void *w)
74
+ static void mosquitto_stop_waiting_for_callbacks(void *c)
73
75
  {
74
- mosquitto_callback_waiting_t *waiter = (mosquitto_callback_waiting_t *)w;
75
- mosquitto_client_wrapper *client = waiter->client;
76
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)c;
77
+ mosquitto_callback_waiting_t *waiter = client->waiter;
78
+ mosquitto_callback_t *callback = NULL;
76
79
 
77
80
  pthread_mutex_lock(&client->callback_mutex);
78
81
  waiter->abort = 1;
82
+ while ((callback = mosquitto_callback_queue_pop(client)) != NULL) {
83
+ rb_mosquitto_free_callback(callback);
84
+ }
79
85
  pthread_mutex_unlock(&client->callback_mutex);
80
86
  pthread_cond_signal(&client->callback_cond);
81
87
  }
@@ -168,18 +174,10 @@ static void rb_mosquitto_handle_callback(int *error_tag, mosquitto_callback_t *c
168
174
  args[0] = client->connect_cb;
169
175
  args[1] = (VALUE)1;
170
176
  args[2] = INT2NUM(cb->rc);
171
- switch (cb->rc) {
172
- case 1:
173
- MosquittoError("connection refused (unacceptable protocol version)");
174
- break;
175
- case 2:
176
- MosquittoError("connection refused (identifier rejected)");
177
- break;
178
- case 3:
179
- MosquittoError("connection refused (broker unavailable)");
180
- break;
181
- default:
182
- rb_mosquitto_funcall_protected(error_tag, args);
177
+ if (cb->rc == 0) {
178
+ rb_mosquitto_funcall_protected(error_tag, args);
179
+ } else {
180
+ MosquittoError(mosquitto_connack_string(cb->rc));
183
181
  }
184
182
  }
185
183
  break;
@@ -269,19 +267,17 @@ static void rb_mosquitto_run_callback(mosquitto_callback_t *callback)
269
267
  static VALUE rb_mosquitto_callback_thread(void *obj)
270
268
  {
271
269
  mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)obj;
272
- mosquitto_callback_waiting_t waiter;
273
- waiter.callback = NULL;
274
- waiter.abort = 0;
275
- waiter.client = client;
276
- while (!waiter.abort)
270
+ mosquitto_callback_waiting_t *waiter = client->waiter;
271
+ waiter->callback = NULL;
272
+ waiter->abort = 0;
273
+ while (!waiter->abort)
277
274
  {
278
- rb_thread_call_without_gvl(mosquitto_wait_for_callbacks, (void *)&waiter, mosquitto_stop_waiting_for_callbacks, (void *)&waiter);
279
- if (waiter.callback)
275
+ rb_thread_call_without_gvl(mosquitto_wait_for_callbacks, (void *)client, mosquitto_stop_waiting_for_callbacks, (void *)client);
276
+ if (waiter->callback)
280
277
  {
281
- rb_mosquitto_run_callback(waiter.callback);
278
+ rb_mosquitto_run_callback(waiter->callback);
282
279
  }
283
280
  }
284
-
285
281
  return Qnil;
286
282
  }
287
283
 
@@ -444,7 +440,14 @@ static void rb_mosquitto_free_client(void *ptr)
444
440
  {
445
441
  mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)ptr;
446
442
  if (client) {
447
- mosquitto_destroy(client->mosq);
443
+ if (client->mosq != NULL) {
444
+ if (!NIL_P(client->callback_thread)) {
445
+ mosquitto_stop_waiting_for_callbacks(client);
446
+ mosquitto_loop_stop(client->mosq, true);
447
+ rb_mosquitto_client_reap_event_thread(client);
448
+ }
449
+ if (client->mosq != NULL) mosquitto_destroy(client->mosq);
450
+ }
448
451
  xfree(client);
449
452
  }
450
453
  }
@@ -508,6 +511,7 @@ static VALUE rb_mosquitto_client_s_new(int argc, VALUE *argv, VALUE client)
508
511
  cl->log_cb = Qnil;
509
512
  cl->callback_thread = Qnil;
510
513
  cl->callback_queue = NULL;
514
+ cl->waiter = NULL;
511
515
  rb_obj_call_init(client, 0, NULL);
512
516
  return client;
513
517
  }
@@ -688,7 +692,7 @@ static VALUE rb_mosquitto_client_auth(VALUE obj, VALUE username, VALUE password)
688
692
 
689
693
  /*
690
694
  * call-seq:
691
- * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key') -> Boolean
695
+ * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key', 'password') -> Boolean
692
696
  *
693
697
  * Configure the client for certificate based SSL/TLS support.
694
698
  *
@@ -714,7 +718,7 @@ static VALUE rb_mosquitto_client_auth(VALUE obj, VALUE username, VALUE password)
714
718
  * @raise [Mosquitto::Error] on invalid input params or when TLS is not supported
715
719
  * @note This must be called before calling Mosquitto::Client#connect
716
720
  * @example
717
- * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key')
721
+ * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key', 'password')
718
722
  *
719
723
  */
720
724
  static VALUE rb_mosquitto_client_tls_set(VALUE obj, VALUE cafile, VALUE capath, VALUE certfile, VALUE keyfile, VALUE password)
@@ -1175,13 +1179,17 @@ static void *rb_mosquitto_client_disconnect_nogvl(void *ptr)
1175
1179
  static VALUE rb_mosquitto_client_disconnect(VALUE obj)
1176
1180
  {
1177
1181
  int ret;
1182
+ bool retried = false;
1183
+ struct timeval time;
1178
1184
  MosquittoGetClient(obj);
1185
+ retry_once:
1179
1186
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_disconnect_nogvl, (void *)client->mosq, RUBY_UBF_IO, 0);
1180
1187
  switch (ret) {
1181
1188
  case MOSQ_ERR_INVAL:
1182
1189
  MosquittoError("invalid input params");
1183
1190
  break;
1184
1191
  case MOSQ_ERR_NO_CONN:
1192
+ RetryNotConnectedOnce();
1185
1193
  MosquittoError("client not connected to broker");
1186
1194
  break;
1187
1195
  default:
@@ -1219,6 +1227,8 @@ static VALUE rb_mosquitto_client_publish(VALUE obj, VALUE mid, VALUE topic, VALU
1219
1227
  {
1220
1228
  struct nogvl_publish_args args;
1221
1229
  int ret, msg_id;
1230
+ struct timeval time;
1231
+ bool retried = false;
1222
1232
  MosquittoGetClient(obj);
1223
1233
  Check_Type(topic, T_STRING);
1224
1234
  MosquittoEncode(topic);
@@ -1236,6 +1246,7 @@ static VALUE rb_mosquitto_client_publish(VALUE obj, VALUE mid, VALUE topic, VALU
1236
1246
  args.payload = (const char *)(args.payloadlen == 0 ? NULL : StringValueCStr(payload));
1237
1247
  args.qos = NUM2INT(qos);
1238
1248
  args.retain = (retain == Qtrue) ? true : false;
1249
+ retry_once:
1239
1250
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_publish_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1240
1251
  switch (ret) {
1241
1252
  case MOSQ_ERR_INVAL:
@@ -1245,6 +1256,7 @@ static VALUE rb_mosquitto_client_publish(VALUE obj, VALUE mid, VALUE topic, VALU
1245
1256
  rb_memerror();
1246
1257
  break;
1247
1258
  case MOSQ_ERR_NO_CONN:
1259
+ RetryNotConnectedOnce();
1248
1260
  MosquittoError("client not connected to broker");
1249
1261
  break;
1250
1262
  case MOSQ_ERR_PROTOCOL:
@@ -1286,6 +1298,8 @@ static VALUE rb_mosquitto_client_subscribe(VALUE obj, VALUE mid, VALUE subscript
1286
1298
  {
1287
1299
  struct nogvl_subscribe_args args;
1288
1300
  int ret, msg_id;
1301
+ struct timeval time;
1302
+ bool retried = false;
1289
1303
  MosquittoGetClient(obj);
1290
1304
  Check_Type(subscription, T_STRING);
1291
1305
  MosquittoEncode(subscription);
@@ -1298,6 +1312,7 @@ static VALUE rb_mosquitto_client_subscribe(VALUE obj, VALUE mid, VALUE subscript
1298
1312
  args.mid = NIL_P(mid) ? NULL : &msg_id;
1299
1313
  args.subscription = StringValueCStr(subscription);
1300
1314
  args.qos = NUM2INT(qos);
1315
+ retry_once:
1301
1316
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_subscribe_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1302
1317
  switch (ret) {
1303
1318
  case MOSQ_ERR_INVAL:
@@ -1307,6 +1322,7 @@ static VALUE rb_mosquitto_client_subscribe(VALUE obj, VALUE mid, VALUE subscript
1307
1322
  rb_memerror();
1308
1323
  break;
1309
1324
  case MOSQ_ERR_NO_CONN:
1325
+ RetryNotConnectedOnce();
1310
1326
  MosquittoError("client not connected to broker");
1311
1327
  break;
1312
1328
  default:
@@ -1340,6 +1356,8 @@ static VALUE rb_mosquitto_client_unsubscribe(VALUE obj, VALUE mid, VALUE subscri
1340
1356
  {
1341
1357
  struct nogvl_subscribe_args args;
1342
1358
  int ret, msg_id;
1359
+ struct timeval time;
1360
+ bool retried = false;
1343
1361
  MosquittoGetClient(obj);
1344
1362
  Check_Type(subscription, T_STRING);
1345
1363
  MosquittoEncode(subscription);
@@ -1350,6 +1368,7 @@ static VALUE rb_mosquitto_client_unsubscribe(VALUE obj, VALUE mid, VALUE subscri
1350
1368
  args.mosq = client->mosq;
1351
1369
  args.mid = NIL_P(mid) ? NULL : &msg_id;
1352
1370
  args.subscription = StringValueCStr(subscription);
1371
+ retry_once:
1353
1372
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_unsubscribe_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1354
1373
  switch (ret) {
1355
1374
  case MOSQ_ERR_INVAL:
@@ -1359,6 +1378,7 @@ static VALUE rb_mosquitto_client_unsubscribe(VALUE obj, VALUE mid, VALUE subscri
1359
1378
  rb_memerror();
1360
1379
  break;
1361
1380
  case MOSQ_ERR_NO_CONN:
1381
+ RetryNotConnectedOnce();
1362
1382
  MosquittoError("client not connected to broker");
1363
1383
  break;
1364
1384
  default:
@@ -1426,12 +1446,15 @@ static VALUE rb_mosquitto_client_loop(VALUE obj, VALUE timeout, VALUE max_packet
1426
1446
  {
1427
1447
  struct nogvl_loop_args args;
1428
1448
  int ret;
1449
+ struct timeval time;
1450
+ bool retried = false;
1429
1451
  MosquittoGetClient(obj);
1430
1452
  Check_Type(timeout, T_FIXNUM);
1431
1453
  Check_Type(max_packets, T_FIXNUM);
1432
1454
  args.mosq = client->mosq;
1433
1455
  args.timeout = NUM2INT(timeout);
1434
1456
  args.max_packets = NUM2INT(max_packets);
1457
+ retry_once:
1435
1458
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1436
1459
  switch (ret) {
1437
1460
  case MOSQ_ERR_INVAL:
@@ -1441,6 +1464,7 @@ static VALUE rb_mosquitto_client_loop(VALUE obj, VALUE timeout, VALUE max_packet
1441
1464
  rb_memerror();
1442
1465
  break;
1443
1466
  case MOSQ_ERR_NO_CONN:
1467
+ RetryNotConnectedOnce();
1444
1468
  MosquittoError("client not connected to broker");
1445
1469
  break;
1446
1470
  case MOSQ_ERR_CONN_LOST:
@@ -1493,12 +1517,15 @@ static VALUE rb_mosquitto_client_loop_forever(VALUE obj, VALUE timeout, VALUE ma
1493
1517
  {
1494
1518
  struct nogvl_loop_args args;
1495
1519
  int ret;
1520
+ struct timeval time;
1521
+ bool retried = false;
1496
1522
  MosquittoGetClient(obj);
1497
1523
  Check_Type(timeout, T_FIXNUM);
1498
1524
  Check_Type(max_packets, T_FIXNUM);
1499
1525
  args.mosq = client->mosq;
1500
1526
  args.timeout = NUM2INT(timeout);
1501
1527
  args.max_packets = NUM2INT(max_packets);
1528
+ retry_once:
1502
1529
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_forever_nogvl, (void *)&args, rb_mosquitto_client_loop_forever_ubf, client);
1503
1530
  switch (ret) {
1504
1531
  case MOSQ_ERR_INVAL:
@@ -1508,6 +1535,7 @@ static VALUE rb_mosquitto_client_loop_forever(VALUE obj, VALUE timeout, VALUE ma
1508
1535
  rb_memerror();
1509
1536
  break;
1510
1537
  case MOSQ_ERR_NO_CONN:
1538
+ RetryNotConnectedOnce();
1511
1539
  MosquittoError("client not connected to broker");
1512
1540
  break;
1513
1541
  case MOSQ_ERR_CONN_LOST:
@@ -1559,8 +1587,9 @@ static VALUE rb_mosquitto_client_loop_start(VALUE obj)
1559
1587
  MosquittoError("thread support is not available");
1560
1588
  break;
1561
1589
  default:
1562
- pthread_mutex_init(&client->callback_mutex, NULL);
1563
- pthread_cond_init(&client->callback_cond, NULL);
1590
+ client->waiter = MOSQ_ALLOC(mosquitto_callback_waiting_t);
1591
+ if (pthread_mutex_init(&client->callback_mutex, NULL) != 0) MosquittoError("failed to create callback thread mutex");
1592
+ if (pthread_cond_init(&client->callback_cond, NULL) != 0) MosquittoError("failed to create callback thread condition var");
1564
1593
  client->callback_thread = rb_thread_create(rb_mosquitto_callback_thread, client);
1565
1594
  /* Allow the callback thread some startup time */
1566
1595
  time.tv_sec = 0;
@@ -1576,9 +1605,23 @@ static void *rb_mosquitto_client_loop_stop_nogvl(void *ptr)
1576
1605
  return (VALUE)mosquitto_loop_stop(args->mosq, args->force);
1577
1606
  }
1578
1607
 
1608
+ static void rb_mosquitto_client_reap_event_thread(mosquitto_client_wrapper *client)
1609
+ {
1610
+ struct timeval time;
1611
+ mosquitto_stop_waiting_for_callbacks(client);
1612
+ /* Allow the callback thread some shutdown time */
1613
+ time.tv_sec = 0;
1614
+ time.tv_usec = 100 * 1000; /* 0.1 sec */
1615
+ rb_thread_wait_for(time);
1616
+ if (pthread_mutex_destroy(&client->callback_mutex) == EINVAL) MosquittoError("could not destroy callback thread mutex");
1617
+ if (pthread_cond_destroy(&client->callback_cond) == EINVAL) MosquittoError("could not destroy callback condition var");
1618
+ xfree(client->waiter);
1619
+ client->callback_thread = Qnil;
1620
+ }
1621
+
1579
1622
  /*
1580
1623
  * call-seq:
1581
- * client.loop_start -> Boolean
1624
+ * client.loop_stop(true) -> Boolean
1582
1625
  *
1583
1626
  * This is part of the threaded client interface. Call this once to stop the
1584
1627
  * network thread previously created with Mosquitto::Client#loop_start. This call
@@ -1591,7 +1634,7 @@ static void *rb_mosquitto_client_loop_stop_nogvl(void *ptr)
1591
1634
  * @return [true] on success
1592
1635
  * @raise [Mosquitto::Error] on invalid input params or if thread support is not available
1593
1636
  * @example
1594
- * client.loop_start
1637
+ * client.loop_stop(true)
1595
1638
  *
1596
1639
  */
1597
1640
  static VALUE rb_mosquitto_client_loop_stop(VALUE obj, VALUE force)
@@ -1604,16 +1647,13 @@ static VALUE rb_mosquitto_client_loop_stop(VALUE obj, VALUE force)
1604
1647
  ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_stop_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1605
1648
  switch (ret) {
1606
1649
  case MOSQ_ERR_INVAL:
1607
- MosquittoError("invalid input params");
1650
+ MosquittoError("Threaded main loop not running for this client. Are you sure you haven't already called Mosquitto::Client#loop_stop ?");
1608
1651
  break;
1609
1652
  case MOSQ_ERR_NOT_SUPPORTED :
1610
1653
  MosquittoError("thread support is not available");
1611
1654
  break;
1612
1655
  default:
1613
- pthread_mutex_destroy(&client->callback_mutex);
1614
- pthread_cond_destroy(&client->callback_cond);
1615
- rb_thread_kill(client->callback_thread);
1616
- client->callback_thread = Qnil;
1656
+ rb_mosquitto_client_reap_event_thread(client);
1617
1657
  return Qtrue;
1618
1658
  }
1619
1659
  }
@@ -1784,6 +1824,30 @@ static VALUE rb_mosquitto_client_want_write(VALUE obj)
1784
1824
  return (ret == true) ? Qtrue : Qfalse;
1785
1825
  }
1786
1826
 
1827
+ /*
1828
+ * call-seq:
1829
+ * client.destroy -> Boolean
1830
+ *
1831
+ * Free memory associated with a mosquitto client instance. Used in integration tests only.
1832
+ *
1833
+ * @return [true] true when memory freed
1834
+ * @example
1835
+ * client.destroy
1836
+ *
1837
+ */
1838
+ static VALUE rb_mosquitto_client_destroy(VALUE obj)
1839
+ {
1840
+ MosquittoGetClient(obj);
1841
+ if (!NIL_P(client->callback_thread)) {
1842
+ mosquitto_stop_waiting_for_callbacks(client);
1843
+ mosquitto_loop_stop(client->mosq, true);
1844
+ rb_mosquitto_client_reap_event_thread(client);
1845
+ }
1846
+ mosquitto_destroy(client->mosq);
1847
+ client->mosq = NULL;
1848
+ return Qtrue;
1849
+ }
1850
+
1787
1851
  /*
1788
1852
  * call-seq:
1789
1853
  * client.reconnect_delay_set(2, 10, true) -> Boolean
@@ -2160,4 +2224,8 @@ void _init_rb_mosquitto_client()
2160
2224
  rb_define_method(rb_cMosquittoClient, "on_subscribe", rb_mosquitto_client_on_subscribe, -1);
2161
2225
  rb_define_method(rb_cMosquittoClient, "on_unsubscribe", rb_mosquitto_client_on_unsubscribe, -1);
2162
2226
  rb_define_method(rb_cMosquittoClient, "on_log", rb_mosquitto_client_on_log, -1);
2227
+
2228
+ /* For integration testing only (will) */
2229
+ rb_define_method(rb_cMosquittoClient, "destroy", rb_mosquitto_client_destroy, 0);
2230
+
2163
2231
  }