mosquitto 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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
  }