ffi-nats-core 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/ffi-nats-core.gemspec +8 -0
  3. data/lib/ffi/nats/core/version.rb +1 -1
  4. data/vendor/cnats/CMakeLists.txt +137 -0
  5. data/vendor/cnats/adapters/libevent.h +220 -0
  6. data/vendor/cnats/adapters/libuv.h +472 -0
  7. data/vendor/cnats/examples/CMakeLists.txt +56 -0
  8. data/vendor/cnats/examples/asynctimeout.c +83 -0
  9. data/vendor/cnats/examples/examples.h +322 -0
  10. data/vendor/cnats/examples/libevent-pub.c +136 -0
  11. data/vendor/cnats/examples/libevent-sub.c +104 -0
  12. data/vendor/cnats/examples/libuv-pub.c +120 -0
  13. data/vendor/cnats/examples/libuv-sub.c +114 -0
  14. data/vendor/cnats/examples/publisher.c +62 -0
  15. data/vendor/cnats/examples/queuegroup.c +132 -0
  16. data/vendor/cnats/examples/replier.c +149 -0
  17. data/vendor/cnats/examples/requestor.c +75 -0
  18. data/vendor/cnats/examples/subscriber.c +133 -0
  19. data/vendor/cnats/src/CMakeLists.txt +31 -0
  20. data/vendor/cnats/src/asynccb.c +66 -0
  21. data/vendor/cnats/src/asynccb.h +42 -0
  22. data/vendor/cnats/src/buf.c +246 -0
  23. data/vendor/cnats/src/buf.h +116 -0
  24. data/vendor/cnats/src/comsock.c +474 -0
  25. data/vendor/cnats/src/comsock.h +81 -0
  26. data/vendor/cnats/src/conn.c +2725 -0
  27. data/vendor/cnats/src/conn.h +75 -0
  28. data/vendor/cnats/src/err.h +31 -0
  29. data/vendor/cnats/src/gc.h +27 -0
  30. data/vendor/cnats/src/hash.c +725 -0
  31. data/vendor/cnats/src/hash.h +141 -0
  32. data/vendor/cnats/src/include/n-unix.h +56 -0
  33. data/vendor/cnats/src/include/n-win.h +59 -0
  34. data/vendor/cnats/src/mem.h +20 -0
  35. data/vendor/cnats/src/msg.c +155 -0
  36. data/vendor/cnats/src/msg.h +43 -0
  37. data/vendor/cnats/src/nats.c +1734 -0
  38. data/vendor/cnats/src/nats.h +2024 -0
  39. data/vendor/cnats/src/natsp.h +518 -0
  40. data/vendor/cnats/src/natstime.c +79 -0
  41. data/vendor/cnats/src/natstime.h +27 -0
  42. data/vendor/cnats/src/nuid.c +265 -0
  43. data/vendor/cnats/src/nuid.h +21 -0
  44. data/vendor/cnats/src/opts.c +1030 -0
  45. data/vendor/cnats/src/opts.h +19 -0
  46. data/vendor/cnats/src/parser.c +869 -0
  47. data/vendor/cnats/src/parser.h +87 -0
  48. data/vendor/cnats/src/pub.c +293 -0
  49. data/vendor/cnats/src/srvpool.c +380 -0
  50. data/vendor/cnats/src/srvpool.h +71 -0
  51. data/vendor/cnats/src/stats.c +54 -0
  52. data/vendor/cnats/src/stats.h +21 -0
  53. data/vendor/cnats/src/status.c +60 -0
  54. data/vendor/cnats/src/status.h +95 -0
  55. data/vendor/cnats/src/sub.c +956 -0
  56. data/vendor/cnats/src/sub.h +34 -0
  57. data/vendor/cnats/src/timer.c +86 -0
  58. data/vendor/cnats/src/timer.h +57 -0
  59. data/vendor/cnats/src/unix/cond.c +103 -0
  60. data/vendor/cnats/src/unix/mutex.c +107 -0
  61. data/vendor/cnats/src/unix/sock.c +105 -0
  62. data/vendor/cnats/src/unix/thread.c +162 -0
  63. data/vendor/cnats/src/url.c +134 -0
  64. data/vendor/cnats/src/url.h +24 -0
  65. data/vendor/cnats/src/util.c +823 -0
  66. data/vendor/cnats/src/util.h +75 -0
  67. data/vendor/cnats/src/version.h +29 -0
  68. data/vendor/cnats/src/version.h.in +29 -0
  69. data/vendor/cnats/src/win/cond.c +86 -0
  70. data/vendor/cnats/src/win/mutex.c +54 -0
  71. data/vendor/cnats/src/win/sock.c +158 -0
  72. data/vendor/cnats/src/win/strings.c +108 -0
  73. data/vendor/cnats/src/win/thread.c +180 -0
  74. data/vendor/cnats/test/CMakeLists.txt +35 -0
  75. data/vendor/cnats/test/certs/ca.pem +38 -0
  76. data/vendor/cnats/test/certs/client-cert.pem +30 -0
  77. data/vendor/cnats/test/certs/client-key.pem +51 -0
  78. data/vendor/cnats/test/certs/server-cert.pem +31 -0
  79. data/vendor/cnats/test/certs/server-key.pem +51 -0
  80. data/vendor/cnats/test/dylib/CMakeLists.txt +10 -0
  81. data/vendor/cnats/test/dylib/nonats.c +13 -0
  82. data/vendor/cnats/test/list.txt +125 -0
  83. data/vendor/cnats/test/test.c +11655 -0
  84. data/vendor/cnats/test/tls.conf +15 -0
  85. data/vendor/cnats/test/tlsverify.conf +19 -0
  86. metadata +83 -1
@@ -0,0 +1,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
+ }