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.
- checksums.yaml +4 -4
- data/ffi-nats-core.gemspec +8 -0
- data/lib/ffi/nats/core/version.rb +1 -1
- data/vendor/cnats/CMakeLists.txt +137 -0
- data/vendor/cnats/adapters/libevent.h +220 -0
- data/vendor/cnats/adapters/libuv.h +472 -0
- data/vendor/cnats/examples/CMakeLists.txt +56 -0
- data/vendor/cnats/examples/asynctimeout.c +83 -0
- data/vendor/cnats/examples/examples.h +322 -0
- data/vendor/cnats/examples/libevent-pub.c +136 -0
- data/vendor/cnats/examples/libevent-sub.c +104 -0
- data/vendor/cnats/examples/libuv-pub.c +120 -0
- data/vendor/cnats/examples/libuv-sub.c +114 -0
- data/vendor/cnats/examples/publisher.c +62 -0
- data/vendor/cnats/examples/queuegroup.c +132 -0
- data/vendor/cnats/examples/replier.c +149 -0
- data/vendor/cnats/examples/requestor.c +75 -0
- data/vendor/cnats/examples/subscriber.c +133 -0
- data/vendor/cnats/src/CMakeLists.txt +31 -0
- data/vendor/cnats/src/asynccb.c +66 -0
- data/vendor/cnats/src/asynccb.h +42 -0
- data/vendor/cnats/src/buf.c +246 -0
- data/vendor/cnats/src/buf.h +116 -0
- data/vendor/cnats/src/comsock.c +474 -0
- data/vendor/cnats/src/comsock.h +81 -0
- data/vendor/cnats/src/conn.c +2725 -0
- data/vendor/cnats/src/conn.h +75 -0
- data/vendor/cnats/src/err.h +31 -0
- data/vendor/cnats/src/gc.h +27 -0
- data/vendor/cnats/src/hash.c +725 -0
- data/vendor/cnats/src/hash.h +141 -0
- data/vendor/cnats/src/include/n-unix.h +56 -0
- data/vendor/cnats/src/include/n-win.h +59 -0
- data/vendor/cnats/src/mem.h +20 -0
- data/vendor/cnats/src/msg.c +155 -0
- data/vendor/cnats/src/msg.h +43 -0
- data/vendor/cnats/src/nats.c +1734 -0
- data/vendor/cnats/src/nats.h +2024 -0
- data/vendor/cnats/src/natsp.h +518 -0
- data/vendor/cnats/src/natstime.c +79 -0
- data/vendor/cnats/src/natstime.h +27 -0
- data/vendor/cnats/src/nuid.c +265 -0
- data/vendor/cnats/src/nuid.h +21 -0
- data/vendor/cnats/src/opts.c +1030 -0
- data/vendor/cnats/src/opts.h +19 -0
- data/vendor/cnats/src/parser.c +869 -0
- data/vendor/cnats/src/parser.h +87 -0
- data/vendor/cnats/src/pub.c +293 -0
- data/vendor/cnats/src/srvpool.c +380 -0
- data/vendor/cnats/src/srvpool.h +71 -0
- data/vendor/cnats/src/stats.c +54 -0
- data/vendor/cnats/src/stats.h +21 -0
- data/vendor/cnats/src/status.c +60 -0
- data/vendor/cnats/src/status.h +95 -0
- data/vendor/cnats/src/sub.c +956 -0
- data/vendor/cnats/src/sub.h +34 -0
- data/vendor/cnats/src/timer.c +86 -0
- data/vendor/cnats/src/timer.h +57 -0
- data/vendor/cnats/src/unix/cond.c +103 -0
- data/vendor/cnats/src/unix/mutex.c +107 -0
- data/vendor/cnats/src/unix/sock.c +105 -0
- data/vendor/cnats/src/unix/thread.c +162 -0
- data/vendor/cnats/src/url.c +134 -0
- data/vendor/cnats/src/url.h +24 -0
- data/vendor/cnats/src/util.c +823 -0
- data/vendor/cnats/src/util.h +75 -0
- data/vendor/cnats/src/version.h +29 -0
- data/vendor/cnats/src/version.h.in +29 -0
- data/vendor/cnats/src/win/cond.c +86 -0
- data/vendor/cnats/src/win/mutex.c +54 -0
- data/vendor/cnats/src/win/sock.c +158 -0
- data/vendor/cnats/src/win/strings.c +108 -0
- data/vendor/cnats/src/win/thread.c +180 -0
- data/vendor/cnats/test/CMakeLists.txt +35 -0
- data/vendor/cnats/test/certs/ca.pem +38 -0
- data/vendor/cnats/test/certs/client-cert.pem +30 -0
- data/vendor/cnats/test/certs/client-key.pem +51 -0
- data/vendor/cnats/test/certs/server-cert.pem +31 -0
- data/vendor/cnats/test/certs/server-key.pem +51 -0
- data/vendor/cnats/test/dylib/CMakeLists.txt +10 -0
- data/vendor/cnats/test/dylib/nonats.c +13 -0
- data/vendor/cnats/test/list.txt +125 -0
- data/vendor/cnats/test/test.c +11655 -0
- data/vendor/cnats/test/tls.conf +15 -0
- data/vendor/cnats/test/tlsverify.conf +19 -0
- 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
|
+
}
|