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,71 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #ifndef SRVPOOL_H_
4
+ #define SRVPOOL_H_
5
+
6
+ #include "status.h"
7
+ #include "hash.h"
8
+
9
+ // Tracks individual backend servers.
10
+ typedef struct __natsSrv
11
+ {
12
+ natsUrl *url;
13
+ bool didConnect;
14
+ bool isImplicit;
15
+ int reconnects;
16
+ int64_t lastAttempt;
17
+
18
+ } natsSrv;
19
+
20
+ typedef struct __natsSrvPool
21
+ {
22
+ natsSrv **srvrs;
23
+ natsStrHash *urls;
24
+ int size;
25
+ int cap;
26
+
27
+ } natsSrvPool;
28
+
29
+ // This is defined in natsp.h, but natsp.h includes this file. Alternatively,
30
+ // we would need to move the defs above in natsp.h.
31
+ struct __natsOptions;
32
+
33
+ #define natsSrvPool_GetSize(p) ((p)->size)
34
+ #define natsSrvPool_GetSrv(p,i) ((p)->srvrs[(i)])
35
+ #define natsSrvPool_GetSrvUrl(p,i) (natsSrvPool_GetSrv((p),(i))->url)
36
+ #define natsSrvPool_SetSrvDidConnect(p,i,c) (natsSrvPool_GetSrv((p),(i))->didConnect=(c))
37
+ #define natsSrvPool_SetSrvReconnects(p,i,r) (natsSrvPool_GetSrv((p),(i))->reconnects=(r))
38
+
39
+ // Create the server pool using the options given.
40
+ // We will place a Url option first, followed by any
41
+ // Server Options. We will randomize the server pool unlesss
42
+ // the NoRandomize flag is set.
43
+ natsStatus
44
+ natsSrvPool_Create(natsSrvPool **newPool, struct __natsOptions *opts);
45
+
46
+ // Return the server from the pool that has the given 'url'. If the 'index'
47
+ // pointer is not NULL, will place at this location the index of the returned
48
+ // server in the pool.
49
+ natsSrv*
50
+ natsSrvPool_GetCurrentServer(natsSrvPool *pool, const natsUrl *url, int *index);
51
+
52
+ // Pop the current server and put onto the end of the list. Select head of list as long
53
+ // as number of reconnect attempts under MaxReconnect.
54
+ natsSrv*
55
+ natsSrvPool_GetNextServer(natsSrvPool *pool, struct __natsOptions *opts, const natsUrl *ncUrl);
56
+
57
+ // Go through the list of the given URLs and add them to the pool if not already
58
+ // present. If `doShuffle` is true, shuffles the pool if new URLs were added.
59
+ natsStatus
60
+ natsSrvPool_addNewURLs(natsSrvPool *pool, char **urls, int urlCount, bool doShuffle);
61
+
62
+ // Returns an array of servers (as a copy). User is responsible to free the memory.
63
+ natsStatus
64
+ natsSrvPool_GetServers(natsSrvPool *pool, bool implicitOnly, char ***servers, int *count);
65
+
66
+ // Destroy the pool, freeing up all memory used.
67
+ void
68
+ natsSrvPool_Destroy(natsSrvPool *pool);
69
+
70
+
71
+ #endif /* SRVPOOL_H_ */
@@ -0,0 +1,54 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include <stdlib.h>
6
+ #include "status.h"
7
+ #include "stats.h"
8
+ #include "mem.h"
9
+
10
+ natsStatus
11
+ natsStatistics_Create(natsStatistics **newStats)
12
+ {
13
+ natsStatistics *stats = NULL;
14
+
15
+ stats = (natsStatistics*) NATS_CALLOC(1, sizeof(natsStatistics));
16
+ if (stats == NULL)
17
+ return nats_setDefaultError(NATS_NO_MEMORY);
18
+
19
+ *newStats = stats;
20
+
21
+ return NATS_OK;
22
+ }
23
+
24
+ natsStatus
25
+ natsStatistics_GetCounts(natsStatistics *stats,
26
+ uint64_t *inMsgs, uint64_t *inBytes,
27
+ uint64_t *outMsgs, uint64_t *outBytes,
28
+ uint64_t *reconnects)
29
+ {
30
+ if (stats == NULL)
31
+ return nats_setDefaultError(NATS_INVALID_ARG);
32
+
33
+ if (inMsgs != NULL)
34
+ *inMsgs = stats->inMsgs;
35
+ if (inBytes != NULL)
36
+ *inBytes = stats->inBytes;
37
+ if (outMsgs != NULL)
38
+ *outMsgs = stats->outMsgs;
39
+ if (outBytes != NULL)
40
+ *outBytes = stats->outBytes;
41
+ if (reconnects != NULL)
42
+ *reconnects = stats->reconnects;
43
+
44
+ return NATS_OK;
45
+ }
46
+
47
+ void
48
+ natsStatistics_Destroy(natsStatistics *stats)
49
+ {
50
+ if (stats == NULL)
51
+ return;
52
+
53
+ NATS_FREE(stats);
54
+ }
@@ -0,0 +1,21 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+
4
+ #ifndef STATS_H_
5
+ #define STATS_H_
6
+
7
+ #include <stdint.h>
8
+
9
+ #include "status.h"
10
+
11
+ struct __natsStatistics
12
+ {
13
+ uint64_t inMsgs;
14
+ uint64_t outMsgs;
15
+ uint64_t inBytes;
16
+ uint64_t outBytes;
17
+ uint64_t reconnects;
18
+
19
+ };
20
+
21
+ #endif /* STATS_H_ */
@@ -0,0 +1,60 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ //#include <stdio.h>
6
+ //#include <stdlib.h>
7
+
8
+ //#include "status.h"
9
+
10
+ static const char *statusText[] = {
11
+ "OK",
12
+
13
+ "Error",
14
+ "Protocol Error",
15
+ "IO Error",
16
+ "Line too long",
17
+
18
+ "Connection Closed",
19
+ "No server available for connection",
20
+ "Stale Connection",
21
+ "Secure Connection not available",
22
+ "Secure Connection Required",
23
+ "Connection Disconnected",
24
+ "Authentication Violation",
25
+
26
+ "Not Permitted",
27
+ "Not Found",
28
+
29
+ "TCP Address missing",
30
+
31
+ "Invalid Subject",
32
+ "Invalid Argument",
33
+ "Invalid Subscription",
34
+ "Invalid Timeout",
35
+
36
+ "Illegal State",
37
+
38
+ "Slow Consumer, messages dropped",
39
+
40
+ "Maximum Payload Exceeded",
41
+ "Maximum Messages Delivered",
42
+
43
+ "Insufficient Buffer",
44
+
45
+ "No Memory",
46
+
47
+ "System Error",
48
+
49
+ "Timeout",
50
+
51
+ "Initialization Failed",
52
+ "Not Initialized",
53
+
54
+ "SSL Error"
55
+ };
56
+
57
+ const char*
58
+ natsStatus_GetText(natsStatus s) {
59
+ return statusText[(int) s];
60
+ }
@@ -0,0 +1,95 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #ifndef STATUS_H_
4
+ #define STATUS_H_
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ /// The connection state
11
+ typedef enum
12
+ {
13
+ DISCONNECTED = 0, ///< The connection has been disconnected
14
+ CONNECTING, ///< The connection is in the process or connecting
15
+ CONNECTED, ///< The connection is connected
16
+ CLOSED, ///< The connection is closed
17
+ RECONNECTING ///< The connection is in the process or reconnecting
18
+
19
+ } natsConnStatus;
20
+
21
+ /// Status returned by most of the APIs
22
+ typedef enum
23
+ {
24
+ NATS_OK = 0, ///< Success
25
+
26
+ NATS_ERR, ///< Generic error
27
+ NATS_PROTOCOL_ERROR, ///< Error when parsing a protocol message,
28
+ /// or not getting the expected message.
29
+ NATS_IO_ERROR, ///< IO Error (network communication).
30
+ NATS_LINE_TOO_LONG, ///< The protocol message read from the socket
31
+ /// does not fit in the read buffer.
32
+
33
+ NATS_CONNECTION_CLOSED, ///< Operation on this connection failed because
34
+ /// the connection is closed.
35
+ NATS_NO_SERVER, ///< Unable to connect, the server could not be
36
+ /// reached or is not running.
37
+ NATS_STALE_CONNECTION, ///< The server closed our connection because it
38
+ /// did not receive PINGs at the expected interval.
39
+ NATS_SECURE_CONNECTION_WANTED, ///< The client is configured to use TLS, but the
40
+ /// server is not.
41
+ NATS_SECURE_CONNECTION_REQUIRED, ///< The server expects a TLS connection.
42
+ NATS_CONNECTION_DISCONNECTED, ///< The connection was disconnected. Depending on
43
+ /// the configuration, the connection may reconnect.
44
+
45
+ NATS_CONNECTION_AUTH_FAILED, ///< The connection failed due to authentication error.
46
+ NATS_NOT_PERMITTED, ///< The action is not permitted.
47
+ NATS_NOT_FOUND, ///< An action could not complete because something
48
+ /// was not found. So far, this is an internal error.
49
+
50
+ NATS_ADDRESS_MISSING, ///< Incorrect URL. For instance no host specified in
51
+ /// the URL.
52
+
53
+ NATS_INVALID_SUBJECT, ///< Invalid subject, for instance NULL or empty string.
54
+ NATS_INVALID_ARG, ///< An invalid argument is passed to a function. For
55
+ /// instance passing NULL to an API that does not
56
+ /// accept this value.
57
+ NATS_INVALID_SUBSCRIPTION, ///< The call to a subscription function fails because
58
+ /// the subscription has previously been closed.
59
+ NATS_INVALID_TIMEOUT, ///< Timeout must be positive numbers.
60
+
61
+ NATS_ILLEGAL_STATE, ///< An unexpected state, for instance calling
62
+ /// #natsSubscription_NextMsg() on an asynchronous
63
+ /// subscriber.
64
+
65
+ NATS_SLOW_CONSUMER, ///< The maximum number of messages waiting to be
66
+ /// delivered has been reached. Messages are dropped.
67
+
68
+ NATS_MAX_PAYLOAD, ///< Attempt to send a payload larger than the maximum
69
+ /// allowed by the NATS Server.
70
+ NATS_MAX_DELIVERED_MSGS, ///< Attempt to receive more messages than allowed, for
71
+ /// instance because of #natsSubscription_AutoUnsubscribe().
72
+
73
+ NATS_INSUFFICIENT_BUFFER, ///< A buffer is not large enough to accommodate the data.
74
+
75
+ NATS_NO_MEMORY, ///< An operation could not complete because of insufficient
76
+ /// memory.
77
+
78
+ NATS_SYS_ERROR, ///< Some system function returned an error.
79
+
80
+ NATS_TIMEOUT, ///< An operation timed-out. For instance
81
+ /// #natsSubscription_NextMsg().
82
+
83
+ NATS_FAILED_TO_INITIALIZE, ///< The library failed to initialize.
84
+ NATS_NOT_INITIALIZED, ///< The library is not yet initialized.
85
+
86
+ NATS_SSL_ERROR ///< An SSL error occurred when trying to establish a
87
+ /// connection.
88
+
89
+ } natsStatus;
90
+
91
+ #ifdef __cplusplus
92
+ }
93
+ #endif
94
+
95
+ #endif /* STATUS_H_ */
@@ -0,0 +1,956 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include <string.h>
6
+ #include <stdio.h>
7
+
8
+ #include "mem.h"
9
+ #include "conn.h"
10
+ #include "sub.h"
11
+ #include "msg.h"
12
+ #include "util.h"
13
+
14
+ #ifdef DEV_MODE
15
+
16
+ static void _retain(natsSubscription *sub) { sub->refs++; }
17
+ static void _release(natsSubscription *sub) { sub->refs--; }
18
+
19
+ void natsSub_Lock(natsSubscription *sub) { natsMutex_Lock(sub->mu); }
20
+ void natsSub_Unlock(natsSubscription *sub) { natsMutex_Unlock(sub->mu); }
21
+
22
+ #else
23
+
24
+ #define _retain(s) ((s)->refs++)
25
+ #define _release(s) ((s)->refs--)
26
+
27
+ #endif // DEV_MODE
28
+
29
+ #define SUB_DLV_WORKER_LOCK(s) if ((s)->libDlvWorker != NULL) \
30
+ natsMutex_Lock((s)->libDlvWorker->lock)
31
+
32
+ #define SUB_DLV_WORKER_UNLOCK(s) if ((s)->libDlvWorker != NULL) \
33
+ natsMutex_Unlock((s)->libDlvWorker->lock)
34
+
35
+ static void
36
+ _freeSubscription(natsSubscription *sub)
37
+ {
38
+ natsMsg *m;
39
+
40
+ if (sub == NULL)
41
+ return;
42
+
43
+ while ((m = sub->msgList.head) != NULL)
44
+ {
45
+ sub->msgList.head = m->next;
46
+ natsMsg_Destroy(m);
47
+ }
48
+
49
+ NATS_FREE(sub->subject);
50
+ NATS_FREE(sub->queue);
51
+
52
+ if (sub->deliverMsgsThread != NULL)
53
+ {
54
+ natsThread_Detach(sub->deliverMsgsThread);
55
+ natsThread_Destroy(sub->deliverMsgsThread);
56
+ }
57
+ natsTimer_Destroy(sub->timeoutTimer);
58
+ natsCondition_Destroy(sub->cond);
59
+ natsMutex_Destroy(sub->mu);
60
+
61
+ natsConn_release(sub->conn);
62
+
63
+ NATS_FREE(sub);
64
+ }
65
+
66
+ void
67
+ natsSub_retain(natsSubscription *sub)
68
+ {
69
+ natsSub_Lock(sub);
70
+
71
+ sub->refs++;
72
+
73
+ natsSub_Unlock(sub);
74
+ }
75
+ void
76
+
77
+ natsSub_release(natsSubscription *sub)
78
+ {
79
+ int refs = 0;
80
+
81
+ if (sub == NULL)
82
+ return;
83
+
84
+ natsSub_Lock(sub);
85
+
86
+ refs = --(sub->refs);
87
+
88
+ natsSub_Unlock(sub);
89
+
90
+ if (refs == 0)
91
+ _freeSubscription(sub);
92
+ }
93
+
94
+ // _deliverMsgs is used to deliver messages to asynchronous subscribers.
95
+ void
96
+ natsSub_deliverMsgs(void *arg)
97
+ {
98
+ natsSubscription *sub = (natsSubscription*) arg;
99
+ natsConnection *nc = sub->conn;
100
+ natsMsgHandler mcb = sub->msgCb;
101
+ void *mcbClosure = sub->msgCbClosure;
102
+ uint64_t delivered;
103
+ uint64_t max;
104
+ natsMsg *msg;
105
+ int64_t timeout;
106
+ natsStatus s = NATS_OK;
107
+
108
+ // This just servers as a barrier for the creation of this thread.
109
+ natsConn_Lock(nc);
110
+ natsConn_Unlock(nc);
111
+
112
+ natsSub_Lock(sub);
113
+ timeout = sub->timeout;
114
+ natsSub_Unlock(sub);
115
+
116
+ while (true)
117
+ {
118
+ natsSub_Lock(sub);
119
+
120
+ s = NATS_OK;
121
+ while (((msg = sub->msgList.head) == NULL) && !(sub->closed) && (s != NATS_TIMEOUT))
122
+ {
123
+ sub->inWait++;
124
+ if (timeout != 0)
125
+ s = natsCondition_TimedWait(sub->cond, sub->mu, timeout);
126
+ else
127
+ natsCondition_Wait(sub->cond, sub->mu);
128
+ sub->inWait--;
129
+ }
130
+
131
+ if (sub->closed)
132
+ {
133
+ natsSub_Unlock(sub);
134
+ break;
135
+ }
136
+
137
+ // Will happen with timeout subscription
138
+ if (msg == NULL)
139
+ {
140
+ natsSub_Unlock(sub);
141
+ // If subscription timed-out, invoke callback with NULL message.
142
+ if (s == NATS_TIMEOUT)
143
+ (*mcb)(nc, sub, NULL, mcbClosure);
144
+ continue;
145
+ }
146
+
147
+ delivered = ++(sub->delivered);
148
+
149
+ sub->msgList.head = msg->next;
150
+
151
+ if (sub->msgList.tail == msg)
152
+ sub->msgList.tail = NULL;
153
+
154
+ sub->msgList.msgs--;
155
+ sub->msgList.bytes -= msg->dataLen;
156
+
157
+ msg->next = NULL;
158
+
159
+ // Capture this under lock.
160
+ max = sub->max;
161
+
162
+ natsSub_Unlock(sub);
163
+
164
+ if ((max == 0) || (delivered <= max))
165
+ {
166
+ (*mcb)(nc, sub, msg, mcbClosure);
167
+ }
168
+ else
169
+ {
170
+ // We need to destroy the message since the user can't do it
171
+ natsMsg_Destroy(msg);
172
+ }
173
+
174
+ // Don't do 'else' because we need to remove when we have hit
175
+ // the max (after the callback returns).
176
+ if ((max > 0) && (delivered >= max))
177
+ {
178
+ // If we have hit the max for delivered msgs, remove sub.
179
+ natsConn_removeSubscription(nc, sub, true);
180
+ break;
181
+ }
182
+ }
183
+
184
+ natsSub_release(sub);
185
+ }
186
+
187
+ void
188
+ natsSub_close(natsSubscription *sub, bool connectionClosed)
189
+ {
190
+ natsMsgDlvWorker *ldw = NULL;
191
+
192
+ natsSub_Lock(sub);
193
+
194
+ SUB_DLV_WORKER_LOCK(sub);
195
+
196
+ if (!(sub->closed))
197
+ {
198
+ sub->closed = true;
199
+ sub->connClosed = connectionClosed;
200
+
201
+ if (sub->libDlvWorker != NULL)
202
+ {
203
+ // If this is a subscription with timeout, stop the timer.
204
+ if (sub->timeout != 0)
205
+ natsTimer_Stop(sub->timeoutTimer);
206
+
207
+ // Post a control message to wake-up the worker which will
208
+ // ensure that all pending messages for this subscription
209
+ // are removed and the subscription will ultimately be
210
+ // released in the worker thread.
211
+ natsLib_msgDeliveryPostControlMsg(sub);
212
+ }
213
+ else
214
+ natsCondition_Broadcast(sub->cond);
215
+ }
216
+
217
+ SUB_DLV_WORKER_UNLOCK(sub);
218
+
219
+ natsSub_Unlock(sub);
220
+ }
221
+
222
+ static void
223
+ _asyncTimeoutCb(natsTimer *timer, void* closure)
224
+ {
225
+ natsSubscription *sub = (natsSubscription*) closure;
226
+
227
+ // Should not happen, but in case
228
+ if (sub->libDlvWorker == NULL)
229
+ return;
230
+
231
+ SUB_DLV_WORKER_LOCK(sub);
232
+
233
+ // If the subscription is closed, or if we are prevented from posting
234
+ // a "timeout" control message, do nothing.
235
+ if (!sub->closed && !sub->timedOut && !sub->timeoutSuspended)
236
+ {
237
+ // Prevent from scheduling another control message while we are not
238
+ // done with previous one.
239
+ sub->timedOut = true;
240
+
241
+ // Set the timer to a very high value, it will be reset from the
242
+ // worker thread.
243
+ natsTimer_Reset(sub->timeoutTimer, 60*60*1000);
244
+
245
+ // Post a control message to the worker thread.
246
+ natsLib_msgDeliveryPostControlMsg(sub);
247
+ }
248
+
249
+ SUB_DLV_WORKER_UNLOCK(sub);
250
+ }
251
+
252
+ static void
253
+ _asyncTimeoutStopCb(natsTimer *timer, void* closure)
254
+ {
255
+ natsSubscription *sub = (natsSubscription*) closure;
256
+
257
+ natsSub_release(sub);
258
+ }
259
+
260
+ natsStatus
261
+ natsSub_create(natsSubscription **newSub, natsConnection *nc, const char *subj,
262
+ const char *queueGroup, int64_t timeout, natsMsgHandler cb, void *cbClosure)
263
+ {
264
+ natsStatus s = NATS_OK;
265
+ natsSubscription *sub = NULL;
266
+
267
+ sub = (natsSubscription*) NATS_CALLOC(1, sizeof(natsSubscription));
268
+ if (sub == NULL)
269
+ return nats_setDefaultError(NATS_NO_MEMORY);
270
+
271
+ s = natsMutex_Create(&(sub->mu));
272
+ if (s != NATS_OK)
273
+ {
274
+ NATS_FREE(sub);
275
+ return NATS_UPDATE_ERR_STACK(s);
276
+ }
277
+
278
+ natsConn_retain(nc);
279
+
280
+ sub->refs = 1;
281
+ sub->conn = nc;
282
+ sub->timeout = timeout;
283
+ sub->msgCb = cb;
284
+ sub->msgCbClosure = cbClosure;
285
+ sub->msgsLimit = nc->opts->maxPendingMsgs;
286
+ sub->bytesLimit = sub->msgsLimit * 1024;
287
+
288
+ if (sub->bytesLimit <= 0)
289
+ return nats_setError(NATS_INVALID_ARG, "Invalid bytes limit of %d", sub->bytesLimit);
290
+
291
+ sub->subject = NATS_STRDUP(subj);
292
+ if (sub->subject == NULL)
293
+ s = nats_setDefaultError(NATS_NO_MEMORY);
294
+
295
+ if ((s == NATS_OK) && (queueGroup != NULL) && (strlen(queueGroup) > 0))
296
+ {
297
+ sub->queue = NATS_STRDUP(queueGroup);
298
+ if (sub->queue == NULL)
299
+ s = nats_setDefaultError(NATS_NO_MEMORY);
300
+ }
301
+ if (s == NATS_OK)
302
+ s = natsCondition_Create(&(sub->cond));
303
+ if ((s == NATS_OK) && (cb != NULL))
304
+ {
305
+ if (!(nc->opts->libMsgDelivery))
306
+ {
307
+ // Let's not rely on the created thread acquiring the lock that
308
+ // would make it safe to retain only on success.
309
+ _retain(sub);
310
+
311
+ // If we have an async callback, start up a sub specific
312
+ // thread to deliver the messages.
313
+ s = natsThread_Create(&(sub->deliverMsgsThread), natsSub_deliverMsgs,
314
+ (void*) sub);
315
+ if (s != NATS_OK)
316
+ _release(sub);
317
+ }
318
+ else
319
+ {
320
+ _retain(sub);
321
+ s = natsLib_msgDeliveryAssignWorker(sub);
322
+ if ((s == NATS_OK) && (timeout > 0))
323
+ {
324
+ _retain(sub);
325
+ s = natsTimer_Create(&sub->timeoutTimer, _asyncTimeoutCb,
326
+ _asyncTimeoutStopCb, timeout, (void*) sub);
327
+ if (s != NATS_OK)
328
+ _release(sub);
329
+ }
330
+ if (s != NATS_OK)
331
+ _release(sub);
332
+ }
333
+ }
334
+
335
+ if (s == NATS_OK)
336
+ *newSub = sub;
337
+ else
338
+ natsSub_release(sub);
339
+
340
+ return NATS_UPDATE_ERR_STACK(s);
341
+ }
342
+
343
+ /*
344
+ * Expresses interest in the given subject. The subject can have wildcards
345
+ * (partial:*, full:>). Messages will be delivered to the associated
346
+ * natsMsgHandler. If no natsMsgHandler is given, the subscription is a
347
+ * synchronous subscription and can be polled via natsSubscription_NextMsg().
348
+ */
349
+ natsStatus
350
+ natsConnection_Subscribe(natsSubscription **sub, natsConnection *nc, const char *subject,
351
+ natsMsgHandler cb, void *cbClosure)
352
+ {
353
+ natsStatus s;
354
+
355
+ if (cb == NULL)
356
+ return nats_setDefaultError(NATS_INVALID_ARG);
357
+
358
+ s = natsConn_subscribe(sub, nc, subject, NULL, 0, cb, cbClosure);
359
+
360
+ return NATS_UPDATE_ERR_STACK(s);
361
+ }
362
+
363
+ /*
364
+ * Similar to natsConnection_Subscribe() except that a timeout is given.
365
+ * If the subscription has not receive any message for the given timeout,
366
+ * the callback is invoked with a `NULL` message. The subscription can
367
+ * then be destroyed, if not, the callback will be invoked again when
368
+ * a message is received or the subscription times-out again.
369
+ */
370
+ natsStatus
371
+ natsConnection_SubscribeTimeout(natsSubscription **sub, natsConnection *nc, const char *subject,
372
+ int64_t timeout, natsMsgHandler cb, void *cbClosure)
373
+ {
374
+ natsStatus s;
375
+
376
+ if ((cb == NULL) || (timeout <= 0))
377
+ return nats_setDefaultError(NATS_INVALID_ARG);
378
+
379
+ s = natsConn_subscribe(sub, nc, subject, NULL, timeout, cb, cbClosure);
380
+
381
+ return NATS_UPDATE_ERR_STACK(s);
382
+ }
383
+
384
+
385
+ /*
386
+ * natsSubscribeSync is syntactic sugar for natsSubscribe(&sub, nc, subject, NULL).
387
+ */
388
+ natsStatus
389
+ natsConnection_SubscribeSync(natsSubscription **sub, natsConnection *nc, const char *subject)
390
+ {
391
+ natsStatus s;
392
+
393
+ s = natsConn_subscribe(sub, nc, subject, NULL, 0, NULL, NULL);
394
+
395
+ return NATS_UPDATE_ERR_STACK(s);
396
+ }
397
+
398
+ /*
399
+ * Creates an asynchronous queue subscriber on the given subject.
400
+ * All subscribers with the same queue name will form the queue group and
401
+ * only one member of the group will be selected to receive any given
402
+ * message asynchronously.
403
+ */
404
+ natsStatus
405
+ natsConnection_QueueSubscribe(natsSubscription **sub, natsConnection *nc,
406
+ const char *subject, const char *queueGroup,
407
+ natsMsgHandler cb, void *cbClosure)
408
+ {
409
+ natsStatus s;
410
+
411
+ if ((queueGroup == NULL) || (strlen(queueGroup) == 0) || (cb == NULL))
412
+ return nats_setDefaultError(NATS_INVALID_ARG);
413
+
414
+ s = natsConn_subscribe(sub, nc, subject, queueGroup, 0, cb, cbClosure);
415
+
416
+ return NATS_UPDATE_ERR_STACK(s);
417
+ }
418
+
419
+ /*
420
+ * Similar to natsConnection_QueueSubscribe() except that a timeout is given.
421
+ * If the subscription has not receive any message for the given timeout,
422
+ * the callback is invoked with a `NULL` message. The subscription can
423
+ * then be destroyed, if not, the callback will be invoked again when
424
+ * a message is received or the subscription times-out again.
425
+ */
426
+ natsStatus
427
+ natsConnection_QueueSubscribeTimeout(natsSubscription **sub, natsConnection *nc,
428
+ const char *subject, const char *queueGroup,
429
+ int64_t timeout, natsMsgHandler cb, void *cbClosure)
430
+ {
431
+ natsStatus s;
432
+
433
+ if ((queueGroup == NULL) || (strlen(queueGroup) == 0) || (cb == NULL)
434
+ || (timeout <= 0))
435
+ {
436
+ return nats_setDefaultError(NATS_INVALID_ARG);
437
+ }
438
+
439
+ s = natsConn_subscribe(sub, nc, subject, queueGroup, timeout, cb, cbClosure);
440
+
441
+ return NATS_UPDATE_ERR_STACK(s);
442
+ }
443
+
444
+ /*
445
+ * Similar to natsQueueSubscribe except that the subscription is synchronous.
446
+ */
447
+ natsStatus
448
+ natsConnection_QueueSubscribeSync(natsSubscription **sub, natsConnection *nc,
449
+ const char *subject, const char *queueGroup)
450
+ {
451
+ natsStatus s;
452
+
453
+ if ((queueGroup == NULL) || (strlen(queueGroup) == 0))
454
+ return nats_setDefaultError(NATS_INVALID_ARG);
455
+
456
+ s = natsConn_subscribe(sub, nc, subject, queueGroup, 0, NULL, NULL);
457
+
458
+ return NATS_UPDATE_ERR_STACK(s);
459
+ }
460
+
461
+ /*
462
+ * By default, messages that arrive are not immediately delivered. This
463
+ * generally improves performance. However, in case of request-reply,
464
+ * this delay has a negative impact. In such case, call this function
465
+ * to have the subscriber be notified immediately each time a message
466
+ * arrives.
467
+ *
468
+ * DEPRECATED
469
+ */
470
+ natsStatus
471
+ natsSubscription_NoDeliveryDelay(natsSubscription *sub)
472
+ {
473
+ if (sub == NULL)
474
+ return nats_setDefaultError(NATS_INVALID_ARG);
475
+
476
+ return NATS_OK;
477
+ }
478
+
479
+
480
+ /*
481
+ * Return the next message available to a synchronous subscriber or block until
482
+ * one is available. A timeout can be used to return when no message has been
483
+ * delivered.
484
+ */
485
+ natsStatus
486
+ natsSubscription_NextMsg(natsMsg **nextMsg, natsSubscription *sub, int64_t timeout)
487
+ {
488
+ natsStatus s = NATS_OK;
489
+ natsConnection *nc = NULL;
490
+ natsMsg *msg = NULL;
491
+ bool removeSub = false;
492
+ int64_t target = 0;
493
+
494
+ if ((sub == NULL) || (nextMsg == NULL))
495
+ return nats_setDefaultError(NATS_INVALID_ARG);
496
+
497
+ natsSub_Lock(sub);
498
+
499
+ if (sub->connClosed)
500
+ {
501
+ natsSub_Unlock(sub);
502
+
503
+ return nats_setDefaultError(NATS_CONNECTION_CLOSED);
504
+ }
505
+ if (sub->closed)
506
+ {
507
+ if ((sub->max > 0) && (sub->delivered >= sub->max))
508
+ s = NATS_MAX_DELIVERED_MSGS;
509
+ else
510
+ s = NATS_INVALID_SUBSCRIPTION;
511
+
512
+ natsSub_Unlock(sub);
513
+
514
+ return nats_setDefaultError(s);
515
+ }
516
+ if (sub->msgCb != NULL)
517
+ {
518
+ natsSub_Unlock(sub);
519
+
520
+ return nats_setDefaultError(NATS_ILLEGAL_STATE);
521
+ }
522
+ if (sub->slowConsumer)
523
+ {
524
+ sub->slowConsumer = false;
525
+ natsSub_Unlock(sub);
526
+
527
+ return nats_setDefaultError(NATS_SLOW_CONSUMER);
528
+ }
529
+
530
+ nc = sub->conn;
531
+
532
+ if (timeout > 0)
533
+ {
534
+ sub->inWait++;
535
+
536
+ while ((sub->msgList.msgs == 0)
537
+ && (s != NATS_TIMEOUT)
538
+ && !(sub->closed))
539
+ {
540
+ if (target == 0)
541
+ target = nats_Now() + timeout;
542
+
543
+ s = natsCondition_AbsoluteTimedWait(sub->cond, sub->mu, target);
544
+ if (s != NATS_OK)
545
+ s = nats_setDefaultError(s);
546
+ }
547
+
548
+ sub->inWait--;
549
+
550
+ if (sub->closed)
551
+ s = nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
552
+ }
553
+ else
554
+ {
555
+ s = (sub->msgList.msgs == 0 ? NATS_TIMEOUT : NATS_OK);
556
+ if (s != NATS_OK)
557
+ s = nats_setDefaultError(s);
558
+ }
559
+
560
+ if (s == NATS_OK)
561
+ {
562
+ msg = sub->msgList.head;
563
+
564
+ sub->msgList.head = msg->next;
565
+
566
+ if (sub->msgList.tail == msg)
567
+ sub->msgList.tail = NULL;
568
+
569
+ sub->msgList.msgs--;
570
+ sub->msgList.bytes -= msg->dataLen;
571
+
572
+ msg->next = NULL;
573
+
574
+ sub->delivered++;
575
+ if (sub->max > 0)
576
+ {
577
+ if (sub->delivered > sub->max)
578
+ s = nats_setDefaultError(NATS_MAX_DELIVERED_MSGS);
579
+ else if (sub->delivered == sub->max)
580
+ removeSub = true;
581
+ }
582
+ }
583
+ if (s == NATS_OK)
584
+ *nextMsg = msg;
585
+
586
+ natsSub_Unlock(sub);
587
+
588
+ if (removeSub)
589
+ natsConn_removeSubscription(nc, sub, true);
590
+
591
+ return NATS_UPDATE_ERR_STACK(s);
592
+ }
593
+
594
+ static natsStatus
595
+ _unsubscribe(natsSubscription *sub, int max)
596
+ {
597
+ natsStatus s = NATS_OK;
598
+ natsConnection *nc = NULL;
599
+
600
+ if (sub == NULL)
601
+ return nats_setDefaultError(NATS_INVALID_ARG);
602
+
603
+ natsSub_Lock(sub);
604
+
605
+ if (sub->connClosed)
606
+ s = NATS_CONNECTION_CLOSED;
607
+ else if (sub->closed)
608
+ s = NATS_INVALID_SUBSCRIPTION;
609
+
610
+ if (s != NATS_OK)
611
+ {
612
+ natsSub_Unlock(sub);
613
+ return nats_setDefaultError(s);
614
+ }
615
+
616
+ nc = sub->conn;
617
+ _retain(sub);
618
+
619
+ natsSub_Unlock(sub);
620
+
621
+ s = natsConn_unsubscribe(nc, sub, max);
622
+
623
+ natsSub_release(sub);
624
+
625
+ return NATS_UPDATE_ERR_STACK(s);
626
+ }
627
+
628
+ /*
629
+ * Removes interest on the subject. Asynchronous subscription may still have
630
+ * a callback in progress, in that case, the subscription will still be valid
631
+ * until the callback returns.
632
+ */
633
+ natsStatus
634
+ natsSubscription_Unsubscribe(natsSubscription *sub)
635
+ {
636
+ natsStatus s = _unsubscribe(sub, 0);
637
+ return NATS_UPDATE_ERR_STACK(s);
638
+ }
639
+
640
+ /*
641
+ * This call issues an automatic natsSubscription_Unsubscribe that is
642
+ * processed by the server when 'max' messages have been received.
643
+ * This can be useful when sending a request to an unknown number
644
+ * of subscribers.
645
+ */
646
+ natsStatus
647
+ natsSubscription_AutoUnsubscribe(natsSubscription *sub, int max)
648
+ {
649
+ natsStatus s = _unsubscribe(sub, max);
650
+ return NATS_UPDATE_ERR_STACK(s);
651
+ }
652
+
653
+ /*
654
+ * Returns the number of queued messages in the client for this subscription.
655
+ */
656
+ natsStatus
657
+ natsSubscription_QueuedMsgs(natsSubscription *sub, uint64_t *queuedMsgs)
658
+ {
659
+ natsStatus s;
660
+ int msgs = 0;
661
+
662
+ if (queuedMsgs == NULL)
663
+ return nats_setDefaultError(NATS_INVALID_ARG);
664
+
665
+ s = natsSubscription_GetPending(sub, &msgs, NULL);
666
+ if (s == NATS_OK)
667
+ *queuedMsgs = (uint64_t) msgs;
668
+
669
+ return s;
670
+ }
671
+
672
+ natsStatus
673
+ natsSubscription_GetPending(natsSubscription *sub, int *msgs, int *bytes)
674
+ {
675
+ if (sub == NULL)
676
+ return nats_setDefaultError(NATS_INVALID_ARG);
677
+
678
+ natsSub_Lock(sub);
679
+
680
+ if (sub->closed)
681
+ {
682
+ natsSub_Unlock(sub);
683
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
684
+ }
685
+
686
+ SUB_DLV_WORKER_LOCK(sub);
687
+
688
+ if (msgs != NULL)
689
+ *msgs = sub->msgList.msgs;
690
+
691
+ if (bytes != NULL)
692
+ *bytes = sub->msgList.bytes;
693
+
694
+ SUB_DLV_WORKER_UNLOCK(sub);
695
+
696
+ natsSub_Unlock(sub);
697
+
698
+ return NATS_OK;
699
+ }
700
+
701
+ natsStatus
702
+ natsSubscription_SetPendingLimits(natsSubscription *sub, int msgLimit, int bytesLimit)
703
+ {
704
+ if (sub == NULL)
705
+ return nats_setDefaultError(NATS_INVALID_ARG);
706
+
707
+ if ((msgLimit == 0) || (bytesLimit == 0))
708
+ return nats_setError(NATS_INVALID_ARG, "%s",
709
+ "Limits must be either > 0 or negative to specify no limit");
710
+
711
+ natsSub_Lock(sub);
712
+
713
+ if (sub->closed)
714
+ {
715
+ natsSub_Unlock(sub);
716
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
717
+ }
718
+
719
+ SUB_DLV_WORKER_LOCK(sub);
720
+
721
+ sub->msgsLimit = msgLimit;
722
+ sub->bytesLimit = bytesLimit;
723
+
724
+ SUB_DLV_WORKER_UNLOCK(sub);
725
+
726
+ natsSub_Unlock(sub);
727
+
728
+ return NATS_OK;
729
+ }
730
+
731
+ natsStatus
732
+ natsSubscription_GetPendingLimits(natsSubscription *sub, int *msgLimit, int *bytesLimit)
733
+ {
734
+ if (sub == NULL)
735
+ return nats_setDefaultError(NATS_INVALID_ARG);
736
+
737
+ natsSub_Lock(sub);
738
+
739
+ if (sub->closed)
740
+ {
741
+ natsSub_Unlock(sub);
742
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
743
+ }
744
+
745
+ SUB_DLV_WORKER_LOCK(sub);
746
+
747
+ if (msgLimit != NULL)
748
+ *msgLimit = sub->msgsLimit;
749
+
750
+ if (bytesLimit != NULL)
751
+ *bytesLimit = sub->bytesLimit;
752
+
753
+ SUB_DLV_WORKER_UNLOCK(sub);
754
+
755
+ natsSub_Unlock(sub);
756
+
757
+ return NATS_OK;
758
+ }
759
+
760
+ natsStatus
761
+ natsSubscription_GetDelivered(natsSubscription *sub, int64_t *msgs)
762
+ {
763
+ if ((sub == NULL) || (msgs == NULL))
764
+ return nats_setDefaultError(NATS_INVALID_ARG);
765
+
766
+ natsSub_Lock(sub);
767
+
768
+ if (sub->closed)
769
+ {
770
+ natsSub_Unlock(sub);
771
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
772
+ }
773
+
774
+ SUB_DLV_WORKER_LOCK(sub);
775
+
776
+ *msgs = (int64_t) sub->delivered;
777
+
778
+ SUB_DLV_WORKER_UNLOCK(sub);
779
+
780
+ natsSub_Unlock(sub);
781
+
782
+ return NATS_OK;
783
+ }
784
+
785
+ natsStatus
786
+ natsSubscription_GetDropped(natsSubscription *sub, int64_t *msgs)
787
+ {
788
+ if ((sub == NULL) || (msgs == NULL))
789
+ return nats_setDefaultError(NATS_INVALID_ARG);
790
+
791
+ natsSub_Lock(sub);
792
+
793
+ if (sub->closed)
794
+ {
795
+ natsSub_Unlock(sub);
796
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
797
+ }
798
+
799
+ SUB_DLV_WORKER_LOCK(sub);
800
+
801
+ *msgs = sub->dropped;
802
+
803
+ SUB_DLV_WORKER_UNLOCK(sub);
804
+
805
+ natsSub_Unlock(sub);
806
+
807
+ return NATS_OK;
808
+ }
809
+
810
+ natsStatus
811
+ natsSubscription_GetMaxPending(natsSubscription *sub, int *msgs, int *bytes)
812
+ {
813
+ if (sub == NULL)
814
+ return nats_setDefaultError(NATS_INVALID_ARG);
815
+
816
+ natsSub_Lock(sub);
817
+
818
+ if (sub->closed)
819
+ {
820
+ natsSub_Unlock(sub);
821
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
822
+ }
823
+
824
+ SUB_DLV_WORKER_LOCK(sub);
825
+
826
+ if (msgs != NULL)
827
+ *msgs = sub->msgsMax;
828
+
829
+ if (bytes != NULL)
830
+ *bytes = sub->bytesMax;
831
+
832
+ SUB_DLV_WORKER_UNLOCK(sub);
833
+
834
+ natsSub_Unlock(sub);
835
+
836
+ return NATS_OK;
837
+ }
838
+
839
+ natsStatus
840
+ natsSubscription_ClearMaxPending(natsSubscription *sub)
841
+ {
842
+ if (sub == NULL)
843
+ return nats_setDefaultError(NATS_INVALID_ARG);
844
+
845
+ natsSub_Lock(sub);
846
+
847
+ if (sub->closed)
848
+ {
849
+ natsSub_Unlock(sub);
850
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
851
+ }
852
+
853
+ SUB_DLV_WORKER_LOCK(sub);
854
+
855
+ sub->msgsMax = 0;
856
+ sub->bytesMax = 0;
857
+
858
+ SUB_DLV_WORKER_UNLOCK(sub);
859
+
860
+ natsSub_Unlock(sub);
861
+
862
+ return NATS_OK;
863
+ }
864
+
865
+ natsStatus
866
+ natsSubscription_GetStats(natsSubscription *sub,
867
+ int *pendingMsgs,
868
+ int *pendingBytes,
869
+ int *maxPendingMsgs,
870
+ int *maxPendingBytes,
871
+ int64_t *deliveredMsgs,
872
+ int64_t *droppedMsgs)
873
+ {
874
+ if (sub == NULL)
875
+ return nats_setDefaultError(NATS_INVALID_ARG);
876
+
877
+ natsSub_Lock(sub);
878
+
879
+ if (sub->closed)
880
+ {
881
+ natsSub_Unlock(sub);
882
+ return nats_setDefaultError(NATS_INVALID_SUBSCRIPTION);
883
+ }
884
+
885
+ SUB_DLV_WORKER_LOCK(sub);
886
+
887
+ if (pendingMsgs != NULL)
888
+ *pendingMsgs = sub->msgList.msgs;
889
+
890
+ if (pendingBytes != NULL)
891
+ *pendingBytes = sub->msgList.bytes;
892
+
893
+ if (maxPendingMsgs != NULL)
894
+ *maxPendingMsgs = sub->msgsMax;
895
+
896
+ if (maxPendingBytes != NULL)
897
+ *maxPendingBytes = sub->bytesMax;
898
+
899
+ if (deliveredMsgs != NULL)
900
+ *deliveredMsgs = (int) sub->delivered;
901
+
902
+ if (droppedMsgs != NULL)
903
+ *droppedMsgs = sub->dropped;
904
+
905
+ SUB_DLV_WORKER_UNLOCK(sub);
906
+
907
+ natsSub_Unlock(sub);
908
+
909
+ return NATS_OK;
910
+ }
911
+
912
+ /*
913
+ * Returns a boolean indicating whether the subscription is still active.
914
+ * This will return false if the subscription has already been closed,
915
+ * or auto unsubscribed.
916
+ */
917
+ bool
918
+ natsSubscription_IsValid(natsSubscription *sub)
919
+ {
920
+ bool valid = false;
921
+
922
+ if (sub == NULL)
923
+ return false;
924
+
925
+ natsSub_Lock(sub);
926
+
927
+ valid = !(sub->closed);
928
+
929
+ natsSub_Unlock(sub);
930
+
931
+ return valid;
932
+ }
933
+
934
+ /*
935
+ * Destroys the subscription object, freeing up memory.
936
+ * If not already done, this call will removes interest on the subject.
937
+ */
938
+ void
939
+ natsSubscription_Destroy(natsSubscription *sub)
940
+ {
941
+ bool doUnsub = false;
942
+
943
+ if (sub == NULL)
944
+ return;
945
+
946
+ natsSub_Lock(sub);
947
+
948
+ doUnsub = !(sub->closed);
949
+
950
+ natsSub_Unlock(sub);
951
+
952
+ if (doUnsub)
953
+ (void) natsSubscription_Unsubscribe(sub);
954
+
955
+ natsSub_release(sub);
956
+ }