ffi-nats-core 0.3.0 → 0.3.1

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/ffi-nats-core.gemspec +8 -0
  3. data/lib/ffi/nats/core/version.rb +1 -1
  4. data/vendor/cnats/CMakeLists.txt +137 -0
  5. data/vendor/cnats/adapters/libevent.h +220 -0
  6. data/vendor/cnats/adapters/libuv.h +472 -0
  7. data/vendor/cnats/examples/CMakeLists.txt +56 -0
  8. data/vendor/cnats/examples/asynctimeout.c +83 -0
  9. data/vendor/cnats/examples/examples.h +322 -0
  10. data/vendor/cnats/examples/libevent-pub.c +136 -0
  11. data/vendor/cnats/examples/libevent-sub.c +104 -0
  12. data/vendor/cnats/examples/libuv-pub.c +120 -0
  13. data/vendor/cnats/examples/libuv-sub.c +114 -0
  14. data/vendor/cnats/examples/publisher.c +62 -0
  15. data/vendor/cnats/examples/queuegroup.c +132 -0
  16. data/vendor/cnats/examples/replier.c +149 -0
  17. data/vendor/cnats/examples/requestor.c +75 -0
  18. data/vendor/cnats/examples/subscriber.c +133 -0
  19. data/vendor/cnats/src/CMakeLists.txt +31 -0
  20. data/vendor/cnats/src/asynccb.c +66 -0
  21. data/vendor/cnats/src/asynccb.h +42 -0
  22. data/vendor/cnats/src/buf.c +246 -0
  23. data/vendor/cnats/src/buf.h +116 -0
  24. data/vendor/cnats/src/comsock.c +474 -0
  25. data/vendor/cnats/src/comsock.h +81 -0
  26. data/vendor/cnats/src/conn.c +2725 -0
  27. data/vendor/cnats/src/conn.h +75 -0
  28. data/vendor/cnats/src/err.h +31 -0
  29. data/vendor/cnats/src/gc.h +27 -0
  30. data/vendor/cnats/src/hash.c +725 -0
  31. data/vendor/cnats/src/hash.h +141 -0
  32. data/vendor/cnats/src/include/n-unix.h +56 -0
  33. data/vendor/cnats/src/include/n-win.h +59 -0
  34. data/vendor/cnats/src/mem.h +20 -0
  35. data/vendor/cnats/src/msg.c +155 -0
  36. data/vendor/cnats/src/msg.h +43 -0
  37. data/vendor/cnats/src/nats.c +1734 -0
  38. data/vendor/cnats/src/nats.h +2024 -0
  39. data/vendor/cnats/src/natsp.h +518 -0
  40. data/vendor/cnats/src/natstime.c +79 -0
  41. data/vendor/cnats/src/natstime.h +27 -0
  42. data/vendor/cnats/src/nuid.c +265 -0
  43. data/vendor/cnats/src/nuid.h +21 -0
  44. data/vendor/cnats/src/opts.c +1030 -0
  45. data/vendor/cnats/src/opts.h +19 -0
  46. data/vendor/cnats/src/parser.c +869 -0
  47. data/vendor/cnats/src/parser.h +87 -0
  48. data/vendor/cnats/src/pub.c +293 -0
  49. data/vendor/cnats/src/srvpool.c +380 -0
  50. data/vendor/cnats/src/srvpool.h +71 -0
  51. data/vendor/cnats/src/stats.c +54 -0
  52. data/vendor/cnats/src/stats.h +21 -0
  53. data/vendor/cnats/src/status.c +60 -0
  54. data/vendor/cnats/src/status.h +95 -0
  55. data/vendor/cnats/src/sub.c +956 -0
  56. data/vendor/cnats/src/sub.h +34 -0
  57. data/vendor/cnats/src/timer.c +86 -0
  58. data/vendor/cnats/src/timer.h +57 -0
  59. data/vendor/cnats/src/unix/cond.c +103 -0
  60. data/vendor/cnats/src/unix/mutex.c +107 -0
  61. data/vendor/cnats/src/unix/sock.c +105 -0
  62. data/vendor/cnats/src/unix/thread.c +162 -0
  63. data/vendor/cnats/src/url.c +134 -0
  64. data/vendor/cnats/src/url.h +24 -0
  65. data/vendor/cnats/src/util.c +823 -0
  66. data/vendor/cnats/src/util.h +75 -0
  67. data/vendor/cnats/src/version.h +29 -0
  68. data/vendor/cnats/src/version.h.in +29 -0
  69. data/vendor/cnats/src/win/cond.c +86 -0
  70. data/vendor/cnats/src/win/mutex.c +54 -0
  71. data/vendor/cnats/src/win/sock.c +158 -0
  72. data/vendor/cnats/src/win/strings.c +108 -0
  73. data/vendor/cnats/src/win/thread.c +180 -0
  74. data/vendor/cnats/test/CMakeLists.txt +35 -0
  75. data/vendor/cnats/test/certs/ca.pem +38 -0
  76. data/vendor/cnats/test/certs/client-cert.pem +30 -0
  77. data/vendor/cnats/test/certs/client-key.pem +51 -0
  78. data/vendor/cnats/test/certs/server-cert.pem +31 -0
  79. data/vendor/cnats/test/certs/server-key.pem +51 -0
  80. data/vendor/cnats/test/dylib/CMakeLists.txt +10 -0
  81. data/vendor/cnats/test/dylib/nonats.c +13 -0
  82. data/vendor/cnats/test/list.txt +125 -0
  83. data/vendor/cnats/test/test.c +11655 -0
  84. data/vendor/cnats/test/tls.conf +15 -0
  85. data/vendor/cnats/test/tlsverify.conf +19 -0
  86. metadata +83 -1
@@ -0,0 +1,1734 @@
1
+ // Copyright 2015-2016 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include <inttypes.h>
8
+ #include <assert.h>
9
+ #include <stdarg.h>
10
+
11
+ #include "mem.h"
12
+ #include "timer.h"
13
+ #include "util.h"
14
+ #include "asynccb.h"
15
+ #include "conn.h"
16
+ #include "sub.h"
17
+
18
+ #define WAIT_LIB_INITIALIZED \
19
+ natsMutex_Lock(gLib.lock); \
20
+ while (!(gLib.initialized) && !(gLib.initAborted)) \
21
+ natsCondition_Wait(gLib.cond, gLib.lock); \
22
+ natsMutex_Unlock(gLib.lock)
23
+
24
+ typedef struct natsTLError
25
+ {
26
+ natsStatus sts;
27
+ char text[256];
28
+ const char *func[MAX_FRAMES];
29
+ int framesCount;
30
+ int skipUpdate;
31
+
32
+ } natsTLError;
33
+
34
+ typedef struct __natsLibTimers
35
+ {
36
+ natsMutex *lock;
37
+ natsCondition *cond;
38
+ natsThread *thread;
39
+ natsTimer *timers;
40
+ int count;
41
+ bool changed;
42
+ bool shutdown;
43
+
44
+ } natsLibTimers;
45
+
46
+ typedef struct __natsLibAsyncCbs
47
+ {
48
+ natsMutex *lock;
49
+ natsCondition *cond;
50
+ natsThread *thread;
51
+ natsAsyncCbInfo *head;
52
+ natsAsyncCbInfo *tail;
53
+ bool shutdown;
54
+
55
+ } natsLibAsyncCbs;
56
+
57
+ typedef struct __natsGCList
58
+ {
59
+ natsMutex *lock;
60
+ natsCondition *cond;
61
+ natsThread *thread;
62
+ natsGCItem *head;
63
+ bool shutdown;
64
+ bool inWait;
65
+
66
+ } natsGCList;
67
+
68
+ typedef struct __natsLibDlvWorkers
69
+ {
70
+ natsMutex *lock;
71
+ int idx;
72
+ int size;
73
+ int maxSize;
74
+ natsMsgDlvWorker **workers;
75
+
76
+ } natsLibDlvWorkers;
77
+
78
+ typedef struct __natsLib
79
+ {
80
+ // Leave these fields before 'refs'
81
+ natsMutex *lock;
82
+ volatile bool wasOpenedOnce;
83
+ bool sslInitialized;
84
+ natsThreadLocal errTLKey;
85
+ natsThreadLocal sslTLKey;
86
+ bool initialized;
87
+ bool closed;
88
+ // Do not move 'refs' without checking _freeLib()
89
+ int refs;
90
+
91
+ bool initializing;
92
+ bool initAborted;
93
+ bool libHandlingMsgDeliveryByDefault;
94
+
95
+ natsLibTimers timers;
96
+ natsLibAsyncCbs asyncCbs;
97
+ natsLibDlvWorkers dlvWorkers;
98
+
99
+ natsCondition *cond;
100
+
101
+ natsGCList gc;
102
+
103
+ } natsLib;
104
+
105
+ int64_t gLockSpinCount = 2000;
106
+
107
+ static natsInitOnceType gInitOnce = NATS_ONCE_STATIC_INIT;
108
+ static natsLib gLib;
109
+
110
+ static void
111
+ _destroyErrTL(void *localStorage)
112
+ {
113
+ natsTLError *err = (natsTLError*) localStorage;
114
+
115
+ NATS_FREE(err);
116
+ }
117
+
118
+ static void
119
+ _cleanupThreadSSL(void *localStorage)
120
+ {
121
+ #if defined(NATS_HAS_TLS)
122
+ ERR_remove_thread_state(0);
123
+ #endif
124
+ }
125
+
126
+ static void
127
+ _finalCleanup(void)
128
+ {
129
+ int refs = 0;
130
+
131
+ natsMutex_Lock(gLib.lock);
132
+ refs = gLib.refs;
133
+ natsMutex_Unlock(gLib.lock);
134
+
135
+ // If some thread is still around when the process exits and has a
136
+ // reference to the library, then don't do the final cleanup...
137
+ if (refs != 0)
138
+ return;
139
+
140
+ if (gLib.sslInitialized)
141
+ {
142
+ #if defined(NATS_HAS_TLS)
143
+ ERR_free_strings();
144
+ EVP_cleanup();
145
+ CRYPTO_cleanup_all_ex_data();
146
+ ERR_remove_thread_state(0);
147
+ sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
148
+ #endif
149
+ natsThreadLocal_DestroyKey(gLib.sslTLKey);
150
+ }
151
+
152
+ natsThreadLocal_DestroyKey(gLib.errTLKey);
153
+ natsMutex_Destroy(gLib.lock);
154
+ gLib.lock = NULL;
155
+ }
156
+
157
+ static void
158
+ _cleanupThreadLocals(void)
159
+ {
160
+ void *tl = NULL;
161
+
162
+ tl = natsThreadLocal_Get(gLib.errTLKey);
163
+ if (tl != NULL)
164
+ _destroyErrTL(tl);
165
+
166
+ tl = NULL;
167
+
168
+ natsMutex_Lock(gLib.lock);
169
+ if (gLib.sslInitialized)
170
+ {
171
+ tl = natsThreadLocal_Get(gLib.sslTLKey);
172
+ if (tl != NULL)
173
+ _cleanupThreadSSL(tl);
174
+ }
175
+ natsMutex_Unlock(gLib.lock);
176
+ }
177
+
178
+
179
+ #if _WIN32
180
+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
181
+ DWORD fdwReason, // reason called
182
+ LPVOID lpvReserved) // reserved
183
+ {
184
+ switch (fdwReason)
185
+ {
186
+ // The thread of the attached process terminates.
187
+ case DLL_THREAD_DETACH:
188
+ case DLL_PROCESS_DETACH:
189
+ {
190
+ if (!(gLib.wasOpenedOnce))
191
+ break;
192
+
193
+ _cleanupThreadLocals();
194
+
195
+ if (fdwReason == DLL_PROCESS_DETACH)
196
+ _finalCleanup();
197
+ break;
198
+ }
199
+ default:
200
+ break;
201
+ }
202
+
203
+ return TRUE;
204
+ UNREFERENCED_PARAMETER(hinstDLL);
205
+ UNREFERENCED_PARAMETER(lpvReserved);
206
+ }
207
+ #else
208
+ __attribute__((destructor)) void natsLib_Destructor(void)
209
+ {
210
+ if (!(gLib.wasOpenedOnce))
211
+ return;
212
+
213
+ // Destroy thread locals for the current thread.
214
+ _cleanupThreadLocals();
215
+
216
+ // Do the final cleanup if possible
217
+ _finalCleanup();
218
+ }
219
+ #endif
220
+
221
+ static void
222
+ _freeTimers(void)
223
+ {
224
+ natsLibTimers *timers = &(gLib.timers);
225
+
226
+ natsThread_Destroy(timers->thread);
227
+ natsCondition_Destroy(timers->cond);
228
+ natsMutex_Destroy(timers->lock);
229
+ }
230
+
231
+ static void
232
+ _freeAsyncCbs(void)
233
+ {
234
+ natsLibAsyncCbs *cbs = &(gLib.asyncCbs);
235
+
236
+ natsThread_Destroy(cbs->thread);
237
+ natsCondition_Destroy(cbs->cond);
238
+ natsMutex_Destroy(cbs->lock);
239
+ }
240
+
241
+ static void
242
+ _freeGC(void)
243
+ {
244
+ natsGCList *gc = &(gLib.gc);
245
+
246
+ natsThread_Destroy(gc->thread);
247
+ natsCondition_Destroy(gc->cond);
248
+ natsMutex_Destroy(gc->lock);
249
+ }
250
+
251
+ static void
252
+ _freeDlvWorker(natsMsgDlvWorker *worker)
253
+ {
254
+ natsThread_Destroy(worker->thread);
255
+ natsCondition_Destroy(worker->cond);
256
+ natsMutex_Destroy(worker->lock);
257
+ NATS_FREE(worker);
258
+ }
259
+
260
+ static void
261
+ _freeDlvWorkers(void)
262
+ {
263
+ int i;
264
+ natsLibDlvWorkers *workers = &(gLib.dlvWorkers);
265
+
266
+ for (i=0; i<workers->size; i++)
267
+ _freeDlvWorker(workers->workers[i]);
268
+
269
+ NATS_FREE(workers->workers);
270
+ natsMutex_Destroy(workers->lock);
271
+ workers->idx = 0;
272
+ workers->size = 0;
273
+ workers->workers = NULL;
274
+ }
275
+
276
+ static void
277
+ _freeLib(void)
278
+ {
279
+ _freeTimers();
280
+ _freeAsyncCbs();
281
+ _freeGC();
282
+ _freeDlvWorkers();
283
+ natsNUID_free();
284
+
285
+ natsCondition_Destroy(gLib.cond);
286
+
287
+ memset(&(gLib.refs), 0, sizeof(natsLib) - ((char *)&(gLib.refs) - (char*)&gLib));
288
+
289
+ natsMutex_Lock(gLib.lock);
290
+ gLib.closed = false;
291
+ gLib.initialized = false;
292
+ natsMutex_Unlock(gLib.lock);
293
+ }
294
+
295
+ void
296
+ natsLib_Retain(void)
297
+ {
298
+ natsMutex_Lock(gLib.lock);
299
+
300
+ gLib.refs++;
301
+
302
+ natsMutex_Unlock(gLib.lock);
303
+ }
304
+
305
+ void
306
+ natsLib_Release(void)
307
+ {
308
+ int refs = 0;
309
+
310
+ natsMutex_Lock(gLib.lock);
311
+
312
+ refs = --(gLib.refs);
313
+
314
+ natsMutex_Unlock(gLib.lock);
315
+
316
+ if (refs == 0)
317
+ _freeLib();
318
+ }
319
+
320
+ static void
321
+ _doInitOnce(void)
322
+ {
323
+ natsStatus s;
324
+
325
+ memset(&gLib, 0, sizeof(natsLib));
326
+
327
+ s = natsMutex_Create(&(gLib.lock));
328
+ if (s == NATS_OK)
329
+ s = natsThreadLocal_CreateKey(&(gLib.errTLKey), _destroyErrTL);
330
+ if (s != NATS_OK)
331
+ {
332
+ fprintf(stderr, "FATAL ERROR: Unable to initialize library!\n");
333
+ fflush(stderr);
334
+ abort();
335
+ }
336
+
337
+ natsSys_Init();
338
+ }
339
+
340
+ static void
341
+ _insertTimer(natsTimer *t)
342
+ {
343
+ natsTimer *cur = gLib.timers.timers;
344
+ natsTimer *prev = NULL;
345
+
346
+ while ((cur != NULL) && (cur->absoluteTime <= t->absoluteTime))
347
+ {
348
+ prev = cur;
349
+ cur = cur->next;
350
+ }
351
+
352
+ if (cur != NULL)
353
+ {
354
+ t->prev = prev;
355
+ t->next = cur;
356
+ cur->prev = t;
357
+
358
+ if (prev != NULL)
359
+ prev->next = t;
360
+ }
361
+ else if (prev != NULL)
362
+ {
363
+ prev->next = t;
364
+ t->prev = prev;
365
+ t->next = NULL;
366
+ }
367
+
368
+ if (prev == NULL)
369
+ gLib.timers.timers = t;
370
+ }
371
+
372
+ // Locks must be held before entering this function
373
+ static void
374
+ _removeTimer(natsLibTimers *timers, natsTimer *t)
375
+ {
376
+ // Switch flag
377
+ t->stopped = true;
378
+
379
+ // It the timer was in the callback, it has already been removed from the
380
+ // list, so skip that.
381
+ if (!(t->inCallback))
382
+ {
383
+ if (t->prev != NULL)
384
+ t->prev->next = t->next;
385
+ if (t->next != NULL)
386
+ t->next->prev = t->prev;
387
+
388
+ if (t == gLib.timers.timers)
389
+ gLib.timers.timers = t->next;
390
+
391
+ t->prev = NULL;
392
+ t->next = NULL;
393
+ }
394
+
395
+ // Decrease the global count of timers
396
+ timers->count--;
397
+ }
398
+
399
+ void
400
+ nats_resetTimer(natsTimer *t, int64_t newInterval)
401
+ {
402
+ natsLibTimers *timers = &(gLib.timers);
403
+
404
+ natsMutex_Lock(timers->lock);
405
+ natsMutex_Lock(t->mu);
406
+
407
+ // If timer is active, we need first to remove it. This call does the
408
+ // right thing if the timer is in the callback.
409
+ if (!(t->stopped))
410
+ _removeTimer(timers, t);
411
+
412
+ // Bump the timer's global count (it as decreased in the _removeTimers call
413
+ timers->count++;
414
+
415
+ // Switch stopped flag
416
+ t->stopped = false;
417
+
418
+ // Set the new interval (may be same than it was before, but that's ok)
419
+ t->interval = newInterval;
420
+
421
+ // If the timer is in the callback, the insertion and setting of the
422
+ // absolute time will be done by the timer thread when returning from
423
+ // the timer's callback.
424
+ if (!(t->inCallback))
425
+ {
426
+ t->absoluteTime = nats_Now() + t->interval;
427
+ _insertTimer(t);
428
+ }
429
+
430
+ natsMutex_Unlock(t->mu);
431
+
432
+ if (!(timers->changed))
433
+ natsCondition_Signal(timers->cond);
434
+
435
+ timers->changed = true;
436
+
437
+ natsMutex_Unlock(timers->lock);
438
+ }
439
+
440
+ void
441
+ nats_stopTimer(natsTimer *t)
442
+ {
443
+ natsLibTimers *timers = &(gLib.timers);
444
+ bool doCb = false;
445
+
446
+ natsMutex_Lock(timers->lock);
447
+ natsMutex_Lock(t->mu);
448
+
449
+ // If the timer was already stopped, nothing to do.
450
+ if (t->stopped)
451
+ {
452
+ natsMutex_Unlock(t->mu);
453
+ natsMutex_Unlock(timers->lock);
454
+
455
+ return;
456
+ }
457
+
458
+ _removeTimer(timers, t);
459
+
460
+ doCb = (!(t->inCallback) && (t->stopCb != NULL));
461
+
462
+ natsMutex_Unlock(t->mu);
463
+
464
+ if (!(timers->changed))
465
+ natsCondition_Signal(timers->cond);
466
+
467
+ timers->changed = true;
468
+
469
+ natsMutex_Unlock(timers->lock);
470
+
471
+ if (doCb)
472
+ (*(t->stopCb))(t, t->closure);
473
+ }
474
+
475
+ int
476
+ nats_getTimersCount(void)
477
+ {
478
+ int count = 0;
479
+
480
+ natsMutex_Lock(gLib.timers.lock);
481
+
482
+ count = gLib.timers.count;
483
+
484
+ natsMutex_Unlock(gLib.timers.lock);
485
+
486
+ return count;
487
+ }
488
+
489
+ int
490
+ nats_getTimersCountInList(void)
491
+ {
492
+ int count = 0;
493
+ natsTimer *t;
494
+
495
+ natsMutex_Lock(gLib.timers.lock);
496
+
497
+ t = gLib.timers.timers;
498
+ while (t != NULL)
499
+ {
500
+ count++;
501
+ t = t->next;
502
+ }
503
+
504
+ natsMutex_Unlock(gLib.timers.lock);
505
+
506
+ return count;
507
+ }
508
+
509
+
510
+ static void
511
+ _timerThread(void *arg)
512
+ {
513
+ natsLibTimers *timers = &(gLib.timers);
514
+ natsTimer *t = NULL;
515
+ natsStatus s = NATS_OK;
516
+ bool doStopCb;
517
+ int64_t target;
518
+
519
+ WAIT_LIB_INITIALIZED;
520
+
521
+ natsMutex_Lock(timers->lock);
522
+
523
+ while (!(timers->shutdown))
524
+ {
525
+ // Take the first timer that needs to fire.
526
+ t = timers->timers;
527
+
528
+ if (t == NULL)
529
+ {
530
+ // No timer, fire in an hour...
531
+ target = nats_Now() + 3600 * 1000;
532
+ }
533
+ else
534
+ {
535
+ target = t->absoluteTime;
536
+ }
537
+
538
+ timers->changed = false;
539
+
540
+ s = NATS_OK;
541
+
542
+ while (!(timers->shutdown)
543
+ && (s != NATS_TIMEOUT)
544
+ && !(timers->changed))
545
+ {
546
+ s = natsCondition_AbsoluteTimedWait(timers->cond, timers->lock,
547
+ target);
548
+ }
549
+
550
+ if (timers->shutdown)
551
+ break;
552
+
553
+ if ((t == NULL) || timers->changed)
554
+ continue;
555
+
556
+ natsMutex_Lock(t->mu);
557
+
558
+ // Remove timer from the list:
559
+ timers->timers = t->next;
560
+ if (t->next != NULL)
561
+ t->next->prev = NULL;
562
+
563
+ t->prev = NULL;
564
+ t->next = NULL;
565
+
566
+ t->inCallback = true;
567
+
568
+ // Retain the timer, since we are going to release the locks for the
569
+ // callback. The user may "destroy" the timer from there, so we need
570
+ // to be protected with reference counting.
571
+ t->refs++;
572
+
573
+ natsMutex_Unlock(t->mu);
574
+ natsMutex_Unlock(timers->lock);
575
+
576
+ (*(t->cb))(t, t->closure);
577
+
578
+ natsMutex_Lock(timers->lock);
579
+ natsMutex_Lock(t->mu);
580
+
581
+ t->inCallback = false;
582
+
583
+ // Timer may have been stopped from within the callback, or during
584
+ // the window the locks were released.
585
+ doStopCb = (t->stopped && (t->stopCb != NULL));
586
+
587
+ // If not stopped, we need to put it back in our list
588
+ if (!doStopCb)
589
+ {
590
+ // Reset our view of what is the time this timer should fire
591
+ // because:
592
+ // 1- the callback may have taken longer than it should
593
+ // 2- the user may have called Reset() with a new interval
594
+ t->absoluteTime = nats_Now() + t->interval;
595
+ _insertTimer(t);
596
+ }
597
+
598
+ natsMutex_Unlock(t->mu);
599
+ natsMutex_Unlock(timers->lock);
600
+
601
+ if (doStopCb)
602
+ (*(t->stopCb))(t, t->closure);
603
+
604
+ // Compensate for the retain that we made before invoking the timer's
605
+ // callback
606
+ natsTimer_Release(t);
607
+
608
+ natsMutex_Lock(timers->lock);
609
+ }
610
+
611
+ // Process the timers that were left in the list (not stopped) when the
612
+ // library is shutdown.
613
+ while ((t = timers->timers) != NULL)
614
+ {
615
+ natsMutex_Lock(t->mu);
616
+
617
+ // Check if we should invoke the callback. Note that although we are
618
+ // releasing the locks below, a timer present in the list here is
619
+ // guaranteed not to have been stopped (because it would not be in
620
+ // the list otherwise, since there is no chance that it is in the
621
+ // timer's callback). So just check if there is a stopCb to invoke.
622
+ doStopCb = (t->stopCb != NULL);
623
+
624
+ // Remove the timer from the list.
625
+ _removeTimer(timers, t);
626
+
627
+ natsMutex_Unlock(t->mu);
628
+ natsMutex_Unlock(timers->lock);
629
+
630
+ // Invoke the callback now.
631
+ if (doStopCb)
632
+ (*(t->stopCb))(t, t->closure);
633
+
634
+ // No release of the timer here. The user is still responsible to call
635
+ // natsTimer_Destroy().
636
+
637
+ natsMutex_Lock(timers->lock);
638
+ }
639
+
640
+ natsMutex_Unlock(timers->lock);
641
+
642
+ natsLib_Release();
643
+ }
644
+
645
+ static void
646
+ _asyncCbsThread(void *arg)
647
+ {
648
+ natsLibAsyncCbs *asyncCbs = &(gLib.asyncCbs);
649
+ natsAsyncCbInfo *cb = NULL;
650
+ natsConnection *nc = NULL;
651
+
652
+ WAIT_LIB_INITIALIZED;
653
+
654
+ natsMutex_Lock(asyncCbs->lock);
655
+
656
+ while (!(asyncCbs->shutdown))
657
+ {
658
+ while (!(asyncCbs->shutdown)
659
+ && ((cb = asyncCbs->head) == NULL))
660
+ {
661
+ natsCondition_Wait(asyncCbs->cond, asyncCbs->lock);
662
+ }
663
+
664
+ if (asyncCbs->shutdown)
665
+ break;
666
+
667
+ asyncCbs->head = cb->next;
668
+
669
+ if (asyncCbs->tail == cb)
670
+ asyncCbs->tail = NULL;
671
+
672
+ cb->next = NULL;
673
+
674
+ natsMutex_Unlock(asyncCbs->lock);
675
+
676
+ nc = cb->nc;
677
+
678
+ switch (cb->type)
679
+ {
680
+ case ASYNC_CLOSED:
681
+ (*(nc->opts->closedCb))(nc, nc->opts->closedCbClosure);
682
+ break;
683
+ case ASYNC_DISCONNECTED:
684
+ (*(nc->opts->disconnectedCb))(nc, nc->opts->disconnectedCbClosure);
685
+ break;
686
+ case ASYNC_RECONNECTED:
687
+ (*(nc->opts->reconnectedCb))(nc, nc->opts->reconnectedCbClosure);
688
+ break;
689
+ case ASYNC_ERROR:
690
+ (*(nc->opts->asyncErrCb))(nc, cb->sub, cb->err, nc->opts->asyncErrCbClosure);
691
+ break;
692
+ default:
693
+ break;
694
+ }
695
+
696
+ natsAsyncCb_Destroy(cb);
697
+
698
+ natsMutex_Lock(asyncCbs->lock);
699
+ }
700
+
701
+ while ((cb = asyncCbs->head) != NULL)
702
+ {
703
+ asyncCbs->head = cb->next;
704
+
705
+ natsAsyncCb_Destroy(cb);
706
+ }
707
+
708
+ natsMutex_Unlock(asyncCbs->lock);
709
+
710
+ natsLib_Release();
711
+ }
712
+
713
+ natsStatus
714
+ nats_postAsyncCbInfo(natsAsyncCbInfo *info)
715
+ {
716
+ natsMutex_Lock(gLib.asyncCbs.lock);
717
+
718
+ if (gLib.asyncCbs.shutdown)
719
+ {
720
+ natsMutex_Unlock(gLib.asyncCbs.lock);
721
+ return NATS_NOT_INITIALIZED;
722
+ }
723
+
724
+ info->next = NULL;
725
+
726
+ if (gLib.asyncCbs.head == NULL)
727
+ gLib.asyncCbs.head = info;
728
+
729
+ if (gLib.asyncCbs.tail != NULL)
730
+ gLib.asyncCbs.tail->next = info;
731
+
732
+ gLib.asyncCbs.tail = info;
733
+
734
+ natsCondition_Signal(gLib.asyncCbs.cond);
735
+
736
+ natsMutex_Unlock(gLib.asyncCbs.lock);
737
+
738
+ return NATS_OK;
739
+ }
740
+
741
+ static void
742
+ _garbageCollector(void *closure)
743
+ {
744
+ natsGCList *gc = &(gLib.gc);
745
+ natsGCItem *item;
746
+ natsGCItem *list;
747
+
748
+ WAIT_LIB_INITIALIZED;
749
+
750
+ natsMutex_Lock(gc->lock);
751
+
752
+ // Repeat until notified to shutdown.
753
+ while (!(gc->shutdown))
754
+ {
755
+ // Go into wait until we are notified to shutdown
756
+ // or there is something to garbage collect
757
+ gc->inWait = true;
758
+
759
+ while (!(gc->shutdown) && (gc->head == NULL))
760
+ {
761
+ natsCondition_Wait(gc->cond, gc->lock);
762
+ }
763
+
764
+ // Out of the wait. Setting this boolean avoids unnecessary
765
+ // signaling when an item is added to the collector.
766
+ gc->inWait = false;
767
+
768
+ // Do not break out on shutdown here, we want to clear the list,
769
+ // even on exit so that valgrind and the like are happy.
770
+
771
+ // Under the lock, we will switch to a local list and reset the
772
+ // GC's list (so that others can add to the list without contention
773
+ // (at least from the GC itself).
774
+ do
775
+ {
776
+ list = gc->head;
777
+ gc->head = NULL;
778
+
779
+ natsMutex_Unlock(gc->lock);
780
+
781
+ // Now that we are outside of the lock, we can empty the list.
782
+ while ((item = list) != NULL)
783
+ {
784
+ // Pops item from the beginning of the list.
785
+ list = item->next;
786
+ item->next = NULL;
787
+
788
+ // Invoke the freeCb associated with this object
789
+ (*(item->freeCb))((void*) item);
790
+ }
791
+
792
+ natsMutex_Lock(gc->lock);
793
+ }
794
+ while (gc->head != NULL);
795
+ }
796
+
797
+ natsMutex_Unlock(gc->lock);
798
+
799
+ natsLib_Release();
800
+ }
801
+
802
+ bool
803
+ natsGC_collect(natsGCItem *item)
804
+ {
805
+ natsGCList *gc;
806
+ bool signal;
807
+
808
+ // If the object was not setup for garbage collection, return false
809
+ // so the caller frees the object.
810
+ if (item->freeCb == NULL)
811
+ return false;
812
+
813
+ gc = &(gLib.gc);
814
+
815
+ natsMutex_Lock(gc->lock);
816
+
817
+ // We will signal only if the GC is in the condition wait.
818
+ signal = gc->inWait;
819
+
820
+ // Add to the front of the list.
821
+ item->next = gc->head;
822
+
823
+ // Update head.
824
+ gc->head = item;
825
+
826
+ if (signal)
827
+ natsCondition_Signal(gc->cond);
828
+
829
+ natsMutex_Unlock(gc->lock);
830
+
831
+ return true;
832
+ }
833
+
834
+ static void
835
+ _libTearDown(void)
836
+ {
837
+ int i;
838
+
839
+ for (i=0; i<gLib.dlvWorkers.size; i++)
840
+ {
841
+ natsMsgDlvWorker *worker = gLib.dlvWorkers.workers[i];
842
+ if (worker->thread != NULL)
843
+ natsThread_Join(worker->thread);
844
+ }
845
+
846
+ if (gLib.timers.thread != NULL)
847
+ natsThread_Join(gLib.timers.thread);
848
+
849
+ if (gLib.asyncCbs.thread != NULL)
850
+ natsThread_Join(gLib.asyncCbs.thread);
851
+
852
+ if (gLib.gc.thread != NULL)
853
+ natsThread_Join(gLib.gc.thread);
854
+
855
+ natsLib_Release();
856
+ }
857
+
858
+ natsStatus
859
+ nats_Open(int64_t lockSpinCount)
860
+ {
861
+ natsStatus s = NATS_OK;
862
+
863
+ if (!nats_InitOnce(&gInitOnce, _doInitOnce))
864
+ return NATS_FAILED_TO_INITIALIZE;
865
+
866
+ natsMutex_Lock(gLib.lock);
867
+
868
+ if (gLib.closed || gLib.initialized || gLib.initializing)
869
+ {
870
+ if (gLib.closed)
871
+ s = NATS_FAILED_TO_INITIALIZE;
872
+ else if (gLib.initializing)
873
+ s = NATS_ILLEGAL_STATE;
874
+
875
+ natsMutex_Unlock(gLib.lock);
876
+ return s;
877
+ }
878
+
879
+ gLib.initializing = true;
880
+ gLib.initAborted = false;
881
+
882
+ #if defined(_WIN32)
883
+ #else
884
+ signal(SIGPIPE, SIG_IGN);
885
+ #endif
886
+
887
+ srand((unsigned int) nats_NowInNanoSeconds());
888
+
889
+ gLib.refs = 1;
890
+
891
+ // If the caller specifies negative value, then we use the default
892
+ if (lockSpinCount >= 0)
893
+ gLockSpinCount = lockSpinCount;
894
+
895
+ s = natsCondition_Create(&(gLib.cond));
896
+
897
+ if (s == NATS_OK)
898
+ s = natsMutex_Create(&(gLib.timers.lock));
899
+ if (s == NATS_OK)
900
+ s = natsCondition_Create(&(gLib.timers.cond));
901
+ if (s == NATS_OK)
902
+ {
903
+ s = natsThread_Create(&(gLib.timers.thread), _timerThread, NULL);
904
+ if (s == NATS_OK)
905
+ gLib.refs++;
906
+ }
907
+
908
+ if (s == NATS_OK)
909
+ s = natsMutex_Create(&(gLib.asyncCbs.lock));
910
+ if (s == NATS_OK)
911
+ s = natsCondition_Create(&(gLib.asyncCbs.cond));
912
+ if (s == NATS_OK)
913
+ {
914
+ s = natsThread_Create(&(gLib.asyncCbs.thread), _asyncCbsThread, NULL);
915
+ if (s == NATS_OK)
916
+ gLib.refs++;
917
+ }
918
+ if (s == NATS_OK)
919
+ s = natsMutex_Create(&(gLib.gc.lock));
920
+ if (s == NATS_OK)
921
+ s = natsCondition_Create(&(gLib.gc.cond));
922
+ if (s == NATS_OK)
923
+ {
924
+ s = natsThread_Create(&(gLib.gc.thread), _garbageCollector, NULL);
925
+ if (s == NATS_OK)
926
+ gLib.refs++;
927
+ }
928
+ if (s == NATS_OK)
929
+ s = natsNUID_init();
930
+
931
+ if (s == NATS_OK)
932
+ s = natsMutex_Create(&(gLib.dlvWorkers.lock));
933
+ if (s == NATS_OK)
934
+ {
935
+ gLib.libHandlingMsgDeliveryByDefault = (getenv("NATS_DEFAULT_TO_LIB_MSG_DELIVERY") != NULL ? true : false);
936
+ gLib.dlvWorkers.maxSize = 2;
937
+ gLib.dlvWorkers.workers = NATS_CALLOC(gLib.dlvWorkers.maxSize, sizeof(natsMsgDlvWorker*));
938
+ if (gLib.dlvWorkers.workers == NULL)
939
+ s = NATS_NO_MEMORY;
940
+ }
941
+
942
+ if (s == NATS_OK)
943
+ gLib.initialized = true;
944
+
945
+ // In case of success or error, broadcast so that lib's threads
946
+ // can proceed.
947
+ if (gLib.cond != NULL)
948
+ {
949
+ if (s != NATS_OK)
950
+ {
951
+ gLib.initAborted = true;
952
+ gLib.timers.shutdown = true;
953
+ gLib.asyncCbs.shutdown = true;
954
+ gLib.gc.shutdown = true;
955
+ }
956
+ natsCondition_Broadcast(gLib.cond);
957
+ }
958
+
959
+ gLib.initializing = false;
960
+ gLib.wasOpenedOnce = true;
961
+
962
+ natsMutex_Unlock(gLib.lock);
963
+
964
+ if (s != NATS_OK)
965
+ _libTearDown();
966
+
967
+ return s;
968
+ }
969
+
970
+ natsStatus
971
+ natsInbox_Create(natsInbox **newInbox)
972
+ {
973
+ natsStatus s;
974
+ char *inbox = NULL;
975
+ char tmpInbox[NATS_INBOX_PRE_LEN + NUID_BUFFER_LEN + 1];
976
+
977
+ s = nats_Open(-1);
978
+ if (s != NATS_OK)
979
+ return s;
980
+
981
+ sprintf(tmpInbox, "%s", inboxPrefix);
982
+ s = natsNUID_Next(tmpInbox + NATS_INBOX_PRE_LEN, NUID_BUFFER_LEN + 1);
983
+ if (s == NATS_OK)
984
+ {
985
+ inbox = NATS_STRDUP(tmpInbox);
986
+ if (inbox == NULL)
987
+ s = NATS_NO_MEMORY;
988
+ }
989
+ if (s == NATS_OK)
990
+ *newInbox = inbox;
991
+
992
+ return s;
993
+ }
994
+
995
+ natsStatus
996
+ natsInbox_init(char *inbox, int inboxLen)
997
+ {
998
+ natsStatus s;
999
+
1000
+ s = nats_Open(-1);
1001
+ if (s != NATS_OK)
1002
+ return s;
1003
+
1004
+ if (inboxLen < (NATS_INBOX_PRE_LEN + NUID_BUFFER_LEN + 1))
1005
+ return NATS_INSUFFICIENT_BUFFER;
1006
+
1007
+ sprintf(inbox, "%s", inboxPrefix);
1008
+ s = natsNUID_Next(inbox + NATS_INBOX_PRE_LEN, NUID_BUFFER_LEN + 1);
1009
+
1010
+ return s;
1011
+ }
1012
+
1013
+ void
1014
+ natsInbox_Destroy(natsInbox *inbox)
1015
+ {
1016
+ if (inbox == NULL)
1017
+ return;
1018
+
1019
+ NATS_FREE(inbox);
1020
+ }
1021
+
1022
+
1023
+ void
1024
+ nats_Close(void)
1025
+ {
1026
+ int i;
1027
+
1028
+ // This is to protect against a call to nats_Close() while there
1029
+ // was no prior call to nats_Open(), either directly or indirectly.
1030
+ if (!nats_InitOnce(&gInitOnce, _doInitOnce))
1031
+ return;
1032
+
1033
+ natsMutex_Lock(gLib.lock);
1034
+
1035
+ if (gLib.closed || !(gLib.initialized))
1036
+ {
1037
+ natsMutex_Unlock(gLib.lock);
1038
+ return;
1039
+ }
1040
+
1041
+ gLib.closed = true;
1042
+
1043
+ natsMutex_Lock(gLib.timers.lock);
1044
+ gLib.timers.shutdown = true;
1045
+ natsCondition_Signal(gLib.timers.cond);
1046
+ natsMutex_Unlock(gLib.timers.lock);
1047
+
1048
+ natsMutex_Lock(gLib.asyncCbs.lock);
1049
+ gLib.asyncCbs.shutdown = true;
1050
+ natsCondition_Signal(gLib.asyncCbs.cond);
1051
+ natsMutex_Unlock(gLib.asyncCbs.lock);
1052
+
1053
+ natsMutex_Lock(gLib.gc.lock);
1054
+ gLib.gc.shutdown = true;
1055
+ natsCondition_Signal(gLib.gc.cond);
1056
+ natsMutex_Unlock(gLib.gc.lock);
1057
+
1058
+ natsMutex_Lock(gLib.dlvWorkers.lock);
1059
+ for (i=0; i<gLib.dlvWorkers.size; i++)
1060
+ {
1061
+ natsMsgDlvWorker *worker = gLib.dlvWorkers.workers[i];
1062
+ natsMutex_Lock(worker->lock);
1063
+ worker->shutdown = true;
1064
+ natsCondition_Signal(worker->cond);
1065
+ natsMutex_Unlock(worker->lock);
1066
+ }
1067
+ natsMutex_Unlock(gLib.dlvWorkers.lock);
1068
+
1069
+ natsMutex_Unlock(gLib.lock);
1070
+
1071
+ _libTearDown();
1072
+ }
1073
+
1074
+ const char*
1075
+ nats_GetVersion(void)
1076
+ {
1077
+ return LIB_NATS_VERSION_STRING;
1078
+ }
1079
+
1080
+ uint32_t
1081
+ nats_GetVersionNumber(void)
1082
+ {
1083
+ return LIB_NATS_VERSION_NUMBER;
1084
+ }
1085
+
1086
+ static void
1087
+ _versionGetString(char *buffer, size_t bufLen, uint32_t verNumber)
1088
+ {
1089
+ snprintf(buffer, bufLen, "%d.%d.%d",
1090
+ ((verNumber >> 16) & 0xF),
1091
+ ((verNumber >> 8) & 0xF),
1092
+ (verNumber & 0xF));
1093
+ }
1094
+
1095
+ bool
1096
+ nats_CheckCompatibilityImpl(uint32_t headerReqVerNumber, uint32_t headerVerNumber,
1097
+ const char *headerVerString)
1098
+ {
1099
+ if ((headerVerNumber < LIB_NATS_VERSION_REQUIRED_NUMBER)
1100
+ || (headerReqVerNumber > LIB_NATS_VERSION_NUMBER))
1101
+ {
1102
+ char reqVerString[10];
1103
+ char libReqVerString[10];
1104
+
1105
+ _versionGetString(reqVerString, sizeof(reqVerString), headerReqVerNumber);
1106
+ _versionGetString(libReqVerString, sizeof(libReqVerString), NATS_VERSION_REQUIRED_NUMBER);
1107
+
1108
+ printf("Incompatible versions:\n" \
1109
+ "Header : %s (requires %s)\n" \
1110
+ "Library: %s (requires %s)\n",
1111
+ headerVerString, reqVerString,
1112
+ NATS_VERSION_STRING, libReqVerString);
1113
+ exit(1);
1114
+ }
1115
+
1116
+ return true;
1117
+ }
1118
+
1119
+ static natsTLError*
1120
+ _getTLError(void)
1121
+ {
1122
+ natsTLError *errTL = NULL;
1123
+ bool needFree = false;
1124
+
1125
+ // The library should already be initialized, but let's protect against
1126
+ // situations where foo() invokes bar(), which invokes baz(), which
1127
+ // invokes nats_Open(). If that last call fails, when we un-wind down
1128
+ // to foo(), it may be difficult to know that nats_Open() failed and
1129
+ // that we should not try to invoke natsLib_setError. So we check again
1130
+ // here that the library has been initialized properly, and if not, we
1131
+ // simply don't set the error.
1132
+ if (nats_Open(-1) != NATS_OK)
1133
+ return NULL;
1134
+
1135
+ errTL = natsThreadLocal_Get(gLib.errTLKey);
1136
+ if (errTL == NULL)
1137
+ {
1138
+ errTL = (natsTLError*) NATS_CALLOC(1, sizeof(natsTLError));
1139
+ if (errTL != NULL)
1140
+ errTL->framesCount = -1;
1141
+ needFree = (errTL != NULL);
1142
+
1143
+ }
1144
+
1145
+ if ((errTL != NULL)
1146
+ && (natsThreadLocal_SetEx(gLib.errTLKey,
1147
+ (const void*) errTL, false) != NATS_OK))
1148
+ {
1149
+ if (needFree)
1150
+ NATS_FREE(errTL);
1151
+
1152
+ errTL = NULL;
1153
+ }
1154
+
1155
+ return errTL;
1156
+ }
1157
+
1158
+ static char*
1159
+ _getErrorShortFileName(const char* fileName)
1160
+ {
1161
+ char *file = strstr(fileName, "src");
1162
+
1163
+ if (file != NULL)
1164
+ file = (file + 4);
1165
+ else
1166
+ file = (char*) fileName;
1167
+
1168
+ return file;
1169
+ }
1170
+
1171
+ static void
1172
+ _updateStack(natsTLError *errTL, const char *funcName, natsStatus errSts,
1173
+ bool calledFromSetError)
1174
+ {
1175
+ int idx;
1176
+
1177
+ idx = errTL->framesCount;
1178
+ if ((idx >= 0)
1179
+ && (idx < MAX_FRAMES)
1180
+ && (strcmp(errTL->func[idx], funcName) == 0))
1181
+ {
1182
+ return;
1183
+ }
1184
+
1185
+ // In case no error was already set...
1186
+ if ((errTL->framesCount == -1) && !calledFromSetError)
1187
+ errTL->sts = errSts;
1188
+
1189
+ idx = ++(errTL->framesCount);
1190
+
1191
+ if (idx >= MAX_FRAMES)
1192
+ return;
1193
+
1194
+ errTL->func[idx] = funcName;
1195
+ }
1196
+
1197
+ natsStatus
1198
+ nats_setErrorReal(const char *fileName, const char *funcName, int line, natsStatus errSts, const void *errTxtFmt, ...)
1199
+ {
1200
+ natsTLError *errTL = _getTLError();
1201
+ char tmp[256];
1202
+ va_list ap;
1203
+ int n;
1204
+
1205
+ if ((errTL == NULL) || errTL->skipUpdate)
1206
+ return errSts;
1207
+
1208
+ errTL->sts = errSts;
1209
+ errTL->framesCount = -1;
1210
+
1211
+ va_start(ap, errTxtFmt);
1212
+ n = vsnprintf(tmp, sizeof(tmp), errTxtFmt, ap);
1213
+ va_end(ap);
1214
+
1215
+ if (n > 0)
1216
+ {
1217
+ snprintf(errTL->text, sizeof(errTL->text), "(%s:%d): %s",
1218
+ _getErrorShortFileName(fileName), line, tmp);
1219
+ }
1220
+
1221
+ _updateStack(errTL, funcName, errSts, true);
1222
+
1223
+ return errSts;
1224
+ }
1225
+
1226
+ natsStatus
1227
+ nats_updateErrStack(natsStatus err, const char *func)
1228
+ {
1229
+ natsTLError *errTL = _getTLError();
1230
+
1231
+ if ((errTL == NULL) || errTL->skipUpdate)
1232
+ return err;
1233
+
1234
+ _updateStack(errTL, func, err, false);
1235
+
1236
+ return err;
1237
+ }
1238
+
1239
+ void
1240
+ nats_clearLastError(void)
1241
+ {
1242
+ natsTLError *errTL = _getTLError();
1243
+
1244
+ if ((errTL == NULL) || errTL->skipUpdate)
1245
+ return;
1246
+
1247
+ errTL->sts = NATS_OK;
1248
+ errTL->text[0] = '\0';
1249
+ errTL->framesCount = -1;
1250
+ }
1251
+
1252
+ void
1253
+ nats_doNotUpdateErrStack(bool skipStackUpdate)
1254
+ {
1255
+ natsTLError *errTL = _getTLError();
1256
+
1257
+ if (errTL == NULL)
1258
+ return;
1259
+
1260
+ if (skipStackUpdate)
1261
+ {
1262
+ errTL->skipUpdate++;
1263
+ }
1264
+ else
1265
+ {
1266
+ errTL->skipUpdate--;
1267
+ assert(errTL->skipUpdate >= 0);
1268
+ }
1269
+ }
1270
+
1271
+ const char*
1272
+ nats_GetLastError(natsStatus *status)
1273
+ {
1274
+ natsStatus s;
1275
+ natsTLError *errTL = NULL;
1276
+
1277
+ if (status != NULL)
1278
+ *status = NATS_OK;
1279
+
1280
+ // Ensure the library is loaded
1281
+ s = nats_Open(-1);
1282
+ if (s != NATS_OK)
1283
+ return NULL;
1284
+
1285
+ errTL = natsThreadLocal_Get(gLib.errTLKey);
1286
+ if ((errTL == NULL) || (errTL->sts == NATS_OK))
1287
+ return NULL;
1288
+
1289
+ if (status != NULL)
1290
+ *status = errTL->sts;
1291
+
1292
+ return errTL->text;
1293
+ }
1294
+
1295
+ natsStatus
1296
+ nats_GetLastErrorStack(char *buffer, size_t bufLen)
1297
+ {
1298
+ natsTLError *errTL = NULL;
1299
+ int offset = 0;
1300
+ int i, max, n, len;
1301
+
1302
+ if ((buffer == NULL) || (bufLen == 0))
1303
+ return NATS_INVALID_ARG;
1304
+
1305
+ buffer[0] = '\0';
1306
+ len = (int) bufLen;
1307
+
1308
+ // Ensure the library is loaded
1309
+ if (nats_Open(-1) != NATS_OK)
1310
+ return NATS_FAILED_TO_INITIALIZE;
1311
+
1312
+ errTL = natsThreadLocal_Get(gLib.errTLKey);
1313
+ if ((errTL == NULL) || (errTL->sts == NATS_OK) || (errTL->framesCount == -1))
1314
+ return NATS_OK;
1315
+
1316
+ max = errTL->framesCount;
1317
+ if (max >= MAX_FRAMES)
1318
+ max = MAX_FRAMES - 1;
1319
+
1320
+ for (i=0; (i<=max) && (len > 0); i++)
1321
+ {
1322
+ n = snprintf(buffer + offset, len, "%s%s",
1323
+ errTL->func[i],
1324
+ (i < max ? "\n" : ""));
1325
+ // On Windows, n will be < 0 if len is not big enough.
1326
+ if (n < 0)
1327
+ {
1328
+ len = 0;
1329
+ }
1330
+ else
1331
+ {
1332
+ offset += n;
1333
+ len -= n;
1334
+ }
1335
+ }
1336
+
1337
+ if ((max != errTL->framesCount) && (len > 0))
1338
+ {
1339
+ n = snprintf(buffer + offset, len, "\n%d more...",
1340
+ errTL->framesCount - max);
1341
+ // On Windows, n will be < 0 if len is not big enough.
1342
+ if (n < 0)
1343
+ len = 0;
1344
+ else
1345
+ len -= n;
1346
+ }
1347
+
1348
+ if (len <= 0)
1349
+ return NATS_INSUFFICIENT_BUFFER;
1350
+
1351
+ return NATS_OK;
1352
+ }
1353
+
1354
+ void
1355
+ nats_PrintLastErrorStack(FILE *file)
1356
+ {
1357
+ natsTLError *errTL = NULL;
1358
+ int i, max;
1359
+
1360
+ // Ensure the library is loaded
1361
+ if (nats_Open(-1) != NATS_OK)
1362
+ return;
1363
+
1364
+ errTL = natsThreadLocal_Get(gLib.errTLKey);
1365
+ if ((errTL == NULL) || (errTL->sts == NATS_OK) || (errTL->framesCount == -1))
1366
+ return;
1367
+
1368
+ fprintf(file, "Error: %d - %s",
1369
+ errTL->sts, natsStatus_GetText(errTL->sts));
1370
+ if (errTL->text[0] != '\0')
1371
+ fprintf(file, " - %s", errTL->text);
1372
+ fprintf(file, "\n");
1373
+ fprintf(file, "Stack: (library version: %s)\n", nats_GetVersion());
1374
+
1375
+ max = errTL->framesCount;
1376
+ if (max >= MAX_FRAMES)
1377
+ max = MAX_FRAMES - 1;
1378
+
1379
+ for (i=0; i<=max; i++)
1380
+ fprintf(file, " %02d - %s\n", (i+1), errTL->func[i]);
1381
+
1382
+ if (max != errTL->framesCount)
1383
+ fprintf(file, " %d more...\n", errTL->framesCount - max);
1384
+
1385
+ fflush(file);
1386
+ }
1387
+
1388
+ void
1389
+ nats_sslRegisterThreadForCleanup(void)
1390
+ {
1391
+ #if defined(NATS_HAS_TLS)
1392
+ // Set anything. The goal is that at thread exit, the thread local key
1393
+ // will have something non NULL associated, which will trigger the
1394
+ // destructor that we have registered.
1395
+ (void) natsThreadLocal_Set(gLib.sslTLKey, (void*) 1);
1396
+ #endif
1397
+ }
1398
+
1399
+ natsStatus
1400
+ nats_sslInit(void)
1401
+ {
1402
+ natsStatus s = NATS_OK;
1403
+
1404
+ // Ensure the library is loaded
1405
+ s = nats_Open(-1);
1406
+ if (s != NATS_OK)
1407
+ return s;
1408
+
1409
+ natsMutex_Lock(gLib.lock);
1410
+
1411
+ if (!(gLib.sslInitialized))
1412
+ {
1413
+ // Regardless of success, mark as initialized so that we
1414
+ // can do cleanup on exit.
1415
+ gLib.sslInitialized = true;
1416
+
1417
+ #if defined(NATS_HAS_TLS)
1418
+ // Initialize SSL.
1419
+ SSL_library_init();
1420
+ SSL_load_error_strings();
1421
+
1422
+ #endif
1423
+ s = natsThreadLocal_CreateKey(&(gLib.sslTLKey), _cleanupThreadSSL);
1424
+ }
1425
+
1426
+ natsMutex_Unlock(gLib.lock);
1427
+
1428
+ return NATS_UPDATE_ERR_STACK(s);
1429
+ }
1430
+
1431
+ static void
1432
+ _deliverMsgs(void *arg)
1433
+ {
1434
+ natsMsgDlvWorker *dlv = (natsMsgDlvWorker*) arg;
1435
+ natsConnection *nc;
1436
+ natsSubscription *sub;
1437
+ natsMsgHandler mcb;
1438
+ void *mcbClosure;
1439
+ uint64_t delivered;
1440
+ uint64_t max;
1441
+ natsMsg *msg;
1442
+ bool timerNeedReset = false;
1443
+
1444
+ natsMutex_Lock(dlv->lock);
1445
+
1446
+ while (true)
1447
+ {
1448
+ while (((msg = dlv->msgList.head) == NULL) && !dlv->shutdown)
1449
+ {
1450
+ dlv->inWait = true;
1451
+ natsCondition_Wait(dlv->cond, dlv->lock);
1452
+ dlv->inWait = false;
1453
+ }
1454
+
1455
+ // Break out only when list is empty
1456
+ if ((msg == NULL) && dlv->shutdown)
1457
+ {
1458
+ break;
1459
+ }
1460
+
1461
+ // Remove message from list now...
1462
+ dlv->msgList.head = msg->next;
1463
+ if (dlv->msgList.tail == msg)
1464
+ dlv->msgList.tail = NULL;
1465
+ msg->next = NULL;
1466
+
1467
+ // Get subscription reference from message
1468
+ sub = msg->sub;
1469
+
1470
+ // Capture these under lock
1471
+ nc = sub->conn;
1472
+ mcb = sub->msgCb;
1473
+ mcbClosure = sub->msgCbClosure;
1474
+ max = sub->max;
1475
+
1476
+ // Is this a control message?
1477
+ if (msg->subject[0] == '\0')
1478
+ {
1479
+ // We need to release this lock...
1480
+ natsMutex_Unlock(dlv->lock);
1481
+
1482
+ // Release the message
1483
+ natsMsg_Destroy(msg);
1484
+
1485
+ if (sub->closed)
1486
+ {
1487
+ // Subscription closed, just release
1488
+ natsSub_release(sub);
1489
+
1490
+ // Grab the lock, we go back to beginning of loop.
1491
+ natsMutex_Lock(dlv->lock);
1492
+ }
1493
+ else if (sub->timedOut)
1494
+ {
1495
+ // Invoke the callback with a NULL message.
1496
+ (*mcb)(nc, sub, NULL, mcbClosure);
1497
+
1498
+ // Grab the lock
1499
+ natsMutex_Lock(dlv->lock);
1500
+
1501
+ // Reset the timedOut boolean to allow for the
1502
+ // subscription to timeout again, and reset the
1503
+ // timer to fire again starting from now.
1504
+ sub->timedOut = false;
1505
+ natsTimer_Reset(sub->timeoutTimer, sub->timeout);
1506
+ }
1507
+
1508
+ // Go back to top of loop.
1509
+ continue;
1510
+ }
1511
+
1512
+ // Update stats before checking closed state
1513
+ sub->msgList.msgs--;
1514
+ sub->msgList.bytes -= msg->dataLen;
1515
+
1516
+ // Need to check for closed subscription again here.
1517
+ // The subscription could have been unsubscribed from a callback
1518
+ // but there were already pending messages. The control message
1519
+ // is queued up. Until it is processed, we need to simply
1520
+ // discard the message and continue.
1521
+ if (sub->closed)
1522
+ {
1523
+ natsMsg_Destroy(msg);
1524
+ continue;
1525
+ }
1526
+
1527
+ delivered = ++(sub->delivered);
1528
+
1529
+ // Is this a subscription that can timeout?
1530
+ if (sub->timeout != 0)
1531
+ {
1532
+ // Prevent the timer to post a timeout control message
1533
+ sub->timeoutSuspended = true;
1534
+
1535
+ // If we are dealing with the last pending message for this sub,
1536
+ // we will reset the timer after the user callback returns.
1537
+ if (sub->msgList.msgs == 0)
1538
+ timerNeedReset = true;
1539
+ }
1540
+
1541
+ natsMutex_Unlock(dlv->lock);
1542
+
1543
+ if ((max == 0) || (delivered <= max))
1544
+ {
1545
+ (*mcb)(nc, sub, msg, mcbClosure);
1546
+ }
1547
+ else
1548
+ {
1549
+ // We need to destroy the message since the user can't do it
1550
+ natsMsg_Destroy(msg);
1551
+ }
1552
+
1553
+ // Don't do 'else' because we need to remove when we have hit
1554
+ // the max (after the callback returns).
1555
+ if ((max > 0) && (delivered >= max))
1556
+ {
1557
+ // If we have hit the max for delivered msgs, remove sub.
1558
+ natsConn_removeSubscription(nc, sub, true);
1559
+ }
1560
+
1561
+ natsMutex_Lock(dlv->lock);
1562
+
1563
+ // Check if timer need to be reset for subscriptions that can timeout.
1564
+ if ((sub->timeout != 0) && timerNeedReset)
1565
+ {
1566
+ timerNeedReset = false;
1567
+
1568
+ // Do this only on timer reset instead of after each return
1569
+ // from callback. The reason is that if there are still pending
1570
+ // messages for this subscription (this is the case otherwise
1571
+ // timerNeedReset would be false), we should prevent
1572
+ // the subscription to timeout anyway.
1573
+ sub->timeoutSuspended = false;
1574
+
1575
+ // Reset the timer to fire in `timeout` from now.
1576
+ natsTimer_Reset(sub->timeoutTimer, sub->timeout);
1577
+ }
1578
+ }
1579
+
1580
+ natsMutex_Unlock(dlv->lock);
1581
+
1582
+ natsLib_Release();
1583
+ }
1584
+
1585
+ natsStatus
1586
+ nats_SetMessageDeliveryPoolSize(int max)
1587
+ {
1588
+ natsStatus s = NATS_OK;
1589
+ natsLibDlvWorkers *workers;
1590
+
1591
+ // Ensure the library is loaded
1592
+ s = nats_Open(-1);
1593
+ if (s != NATS_OK)
1594
+ return s;
1595
+
1596
+ workers = &gLib.dlvWorkers;
1597
+
1598
+ natsMutex_Lock(workers->lock);
1599
+
1600
+ if (max <= 0)
1601
+ {
1602
+ natsMutex_Unlock(workers->lock);
1603
+ return nats_setError(NATS_ERR, "Pool size cannot be negative or zero", "");
1604
+ }
1605
+
1606
+ // Do not error on max < workers->maxSize in case we allow shrinking
1607
+ // the pool in the future.
1608
+ if (max > workers->maxSize)
1609
+ {
1610
+ natsMsgDlvWorker **newArray = NATS_CALLOC(max, sizeof(natsMsgDlvWorker*));
1611
+ if (newArray == NULL)
1612
+ s = nats_setDefaultError(NATS_NO_MEMORY);
1613
+ if (s == NATS_OK)
1614
+ {
1615
+ int i;
1616
+ for (i=0; i<workers->size; i++)
1617
+ newArray[i] = workers->workers[i];
1618
+
1619
+ NATS_FREE(workers->workers);
1620
+ workers->workers = newArray;
1621
+ workers->maxSize = max;
1622
+ }
1623
+ }
1624
+
1625
+ natsMutex_Unlock(workers->lock);
1626
+
1627
+ return NATS_UPDATE_ERR_STACK(s);
1628
+ }
1629
+
1630
+ // Post a control message to the worker's queue.
1631
+ natsStatus
1632
+ natsLib_msgDeliveryPostControlMsg(natsSubscription *sub)
1633
+ {
1634
+ natsStatus s;
1635
+ natsMsg *controlMsg = NULL;
1636
+ natsMsgDlvWorker *worker = (sub->libDlvWorker);
1637
+
1638
+ // Create a "end" message and post it to the delivery worker
1639
+ s = natsMsg_create(&controlMsg, NULL, 0, NULL, 0, NULL, 0);
1640
+ if (s == NATS_OK)
1641
+ {
1642
+ natsMsgList *l;
1643
+
1644
+ natsMutex_Lock(worker->lock);
1645
+
1646
+ controlMsg->sub = sub;
1647
+
1648
+ l = &(worker->msgList);
1649
+ if (l->tail != NULL)
1650
+ l->tail->next = controlMsg;
1651
+ if (l->head == NULL)
1652
+ l->head = controlMsg;
1653
+ l->tail = controlMsg;
1654
+
1655
+ if (worker->inWait)
1656
+ natsCondition_Signal(worker->cond);
1657
+
1658
+ natsMutex_Unlock(worker->lock);
1659
+ }
1660
+ return NATS_UPDATE_ERR_STACK(s);
1661
+ }
1662
+
1663
+ natsStatus
1664
+ natsLib_msgDeliveryAssignWorker(natsSubscription *sub)
1665
+ {
1666
+ natsStatus s = NATS_OK;
1667
+ natsLibDlvWorkers *workers = &(gLib.dlvWorkers);
1668
+ natsMsgDlvWorker *worker = NULL;
1669
+
1670
+ natsMutex_Lock(workers->lock);
1671
+
1672
+ if (workers->maxSize == 0)
1673
+ {
1674
+ natsMutex_Unlock(workers->lock);
1675
+ return nats_setError(NATS_FAILED_TO_INITIALIZE, "Message delivery thread pool size is 0!", "");
1676
+ }
1677
+
1678
+ worker = workers->workers[workers->idx];
1679
+ if (worker == NULL)
1680
+ {
1681
+ worker = NATS_CALLOC(1, sizeof(natsMsgDlvWorker));
1682
+ if (worker == NULL)
1683
+ s = nats_setDefaultError(NATS_NO_MEMORY);
1684
+ if (s == NATS_OK)
1685
+ s = natsMutex_Create(&worker->lock);
1686
+ if (s == NATS_OK)
1687
+ s = natsCondition_Create(&worker->cond);
1688
+ if (s == NATS_OK)
1689
+ {
1690
+ natsLib_Retain();
1691
+ s = natsThread_Create(&worker->thread, _deliverMsgs, (void*) worker);
1692
+ if (s != NATS_OK)
1693
+ natsLib_Release();
1694
+ }
1695
+ if (s == NATS_OK)
1696
+ {
1697
+ workers->workers[workers->idx] = worker;
1698
+ workers->size++;
1699
+ }
1700
+ else
1701
+ {
1702
+ _freeDlvWorker(worker);
1703
+ }
1704
+ }
1705
+ if (s == NATS_OK)
1706
+ {
1707
+ sub->libDlvWorker = worker;
1708
+ if (++(workers->idx) == workers->maxSize)
1709
+ workers->idx = 0;
1710
+ }
1711
+
1712
+ natsMutex_Unlock(workers->lock);
1713
+
1714
+ return NATS_UPDATE_ERR_STACK(s);
1715
+ }
1716
+
1717
+ bool
1718
+ natsLib_isLibHandlingMsgDeliveryByDefault()
1719
+ {
1720
+ return gLib.libHandlingMsgDeliveryByDefault;
1721
+ }
1722
+
1723
+ void
1724
+ natsLib_getMsgDeliveryPoolInfo(int *maxSize, int *size, int *idx, natsMsgDlvWorker ***workersArray)
1725
+ {
1726
+ natsLibDlvWorkers *workers = &gLib.dlvWorkers;
1727
+
1728
+ natsMutex_Lock(workers->lock);
1729
+ *maxSize = workers->maxSize;
1730
+ *size = workers->size;
1731
+ *idx = workers->idx;
1732
+ *workersArray = workers->workers;
1733
+ natsMutex_Unlock(workers->lock);
1734
+ }