ffi-nats-core 0.3.0 → 0.3.1

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