Branch data Line data Source code
1 : : #include <sys/mman.h>
2 : : #include <sys/stat.h>
3 : : #include <fcntl.h>
4 : : #include <unistd.h>
5 : : #include <sys/types.h>
6 : : #include <memory>
7 : : #include <napi.h>
8 : : #include <memory>
9 : : #include <vector>
10 : : #include <unordered_set>
11 : : #include <algorithm>
12 : : #include <limits>
13 : :
14 : : typedef uint64_t sequence_t;
15 : : typedef int32_t status_t;
16 : :
17 : : const sequence_t sequence_max = std::numeric_limits<sequence_t>::max();
18 : :
19 : : // Needs to be heap allocated because we access it from finalizers which can be called
20 : : // on process exit
21 : : static std::unordered_set<uint8_t*> *buffers;
22 : : static std::mutex buffers_mutex;
23 : :
24 : : const int memorder = __ATOMIC_SEQ_CST;
25 : :
26 : : class Disruptor : public Napi::ObjectWrap<Disruptor>
27 : : {
28 : : public:
29 : : Disruptor(const Napi::CallbackInfo& info);
30 : : ~Disruptor();
31 : :
32 : : static Napi::Object Initialize(Napi::Env env, Napi::Object exports);
33 : :
34 : : // Unmap the shared memory. Don't access it again from this Disruptor!
35 : : void Release(const Napi::CallbackInfo& info);
36 : :
37 : : // Return unconsumed slots for a consumer
38 : : Napi::Value ConsumeNew(const Napi::CallbackInfo& info);
39 : : Napi::Value ConsumeNewSync(const Napi::CallbackInfo& info);
40 : :
41 : : // Commit consumed slots
42 : : Napi::Value ConsumeCommit(const Napi::CallbackInfo&);
43 : :
44 : : // Claim a slot for writing a value
45 : : Napi::Value ProduceClaim(const Napi::CallbackInfo& info);
46 : : Napi::Value ProduceClaimSync(const Napi::CallbackInfo& info);
47 : :
48 : : // Claim multiple slots for writing values
49 : : Napi::Value ProduceClaimMany(const Napi::CallbackInfo& info);
50 : : Napi::Value ProduceClaimManySync(const Napi::CallbackInfo& info);
51 : :
52 : : // Claim all available slots for writing values
53 : : Napi::Value ProduceClaimAvail(const Napi::CallbackInfo& info);
54 : : Napi::Value ProduceClaimAvailSync(const Napi::CallbackInfo& info);
55 : :
56 : : // Commit a claimed slot
57 : : Napi::Value ProduceCommit(const Napi::CallbackInfo& info);
58 : : Napi::Value ProduceCommitSync(const Napi::CallbackInfo& info);
59 : :
60 : : // Get slots previously claimed but not committed
61 : : Napi::Value ProduceRecover(const Napi::CallbackInfo& info);
62 : :
63 : : // Get size of each element in bytes
64 : : Napi::Value GetElementSize(const Napi::CallbackInfo& info);
65 : :
66 : : // Get whether not to return while not ready or full
67 : : Napi::Value GetSpin(const Napi::CallbackInfo& info);
68 : :
69 : : // Get status value
70 : : Napi::Value GetStatus(const Napi::CallbackInfo& info);
71 : :
72 : : // Set status value
73 : : void SetStatus(const Napi::CallbackInfo& info, const Napi::Value& value);
74 : :
75 : 3596153 : inline bool Spin()
76 : : {
77 : 3596153 : return spin;
78 : : }
79 : :
80 : : private:
81 : : friend class ConsumeNewAsyncWorker;
82 : : friend class ProduceClaimAsyncWorker;
83 : : friend class ProduceClaimManyAsyncWorker;
84 : : friend class ProduceClaimAvailAsyncWorker;
85 : : friend class ProduceCommitAsyncWorker;
86 : : friend class SyncBuffer;
87 : : friend class AsyncBuffer;
88 : :
89 : : int Release();
90 : :
91 : : void UpdatePending(sequence_t seq_consumer, sequence_t seq_cursor);
92 : : void UpdateSeqNext(const sequence_t seq_next,
93 : : const sequence_t seq_next_end,
94 : : const bool all_ignored);
95 : : uint32_t GetSeqNext(const Napi::CallbackInfo& info,
96 : : sequence_t& seq_next,
97 : : sequence_t& seq_next_end);
98 : :
99 : : template<typename Array, typename DisruptorBuffer>
100 : : void ProduceGetBuffers(const Napi::Env& env,
101 : : const sequence_t seq_next,
102 : : const sequence_t seq_next_end,
103 : : const bool all_ignored,
104 : : Array& r);
105 : :
106 : : template<typename Array, typename DisruptorBuffer>
107 : : Array ConsumeNewSync(const Napi::Env& env, const bool retry, sequence_t& start);
108 : : void ConsumeNewAsync(const Napi::CallbackInfo& info);
109 : :
110 : : bool ConsumeCommit();
111 : :
112 : : template<typename DisruptorBuffer>
113 : : typename DisruptorBuffer::Buffer ProduceClaimSync(const Napi::Env& env,
114 : : const bool retry,
115 : : sequence_t& out_next,
116 : : sequence_t& out_next_end,
117 : : bool& out_all_ignored);
118 : : void ProduceClaimAsync(const Napi::CallbackInfo& info);
119 : :
120 : : template<typename Array, typename DisruptorBuffer>
121 : : Array ProduceClaimManySync(const Napi::Env& env,
122 : : const uint32_t n,
123 : : const bool retry,
124 : : sequence_t& out_next,
125 : : sequence_t& out_next_end,
126 : : bool& out_all_ignored);
127 : : void ProduceClaimManyAsync(const Napi::CallbackInfo& info);
128 : : void ProduceClaimManyAsync(const Napi::CallbackInfo& info,
129 : : const uint32_t n);
130 : :
131 : : template<typename Array, typename DisruptorBuffer>
132 : : Array ProduceClaimAvailSync(const Napi::Env& env,
133 : : const uint32_t max,
134 : : const bool retry,
135 : : sequence_t& out_next,
136 : : sequence_t& out_next_end,
137 : : bool& out_all_ignored);
138 : : void ProduceClaimAvailAsync(const Napi::CallbackInfo& info);
139 : : void ProduceClaimAvailAsync(const Napi::CallbackInfo& info,
140 : : const uint32_t max);
141 : :
142 : : template<typename Boolean>
143 : : Boolean ProduceCommitSync(const Napi::Env& env,
144 : : sequence_t seq_next,
145 : : sequence_t seq_next_end,
146 : : const bool retry);
147 : : void ProduceCommitAsync(const Napi::CallbackInfo& info);
148 : : void ProduceCommitAsync(const Napi::CallbackInfo& info,
149 : : sequence_t seq_next,
150 : : sequence_t seq_next_end,
151 : : const uint32_t cb_arg);
152 : :
153 : : uint32_t num_elements;
154 : : uint32_t element_size;
155 : : uint32_t num_consumers;
156 : : uint32_t consumer;
157 : : bool init;
158 : : bool spin;
159 : :
160 : : size_t shm_size;
161 : : void* shm_buf;
162 : :
163 : : sequence_t *consumers; // for each consumer, next slot to read
164 : : sequence_t *cursor; // next slot to be filled
165 : : sequence_t *next; // next slot to claim
166 : : status_t *status; // status code (app-specific)
167 : : uint8_t* elements;
168 : : sequence_t *ptr_consumer;
169 : :
170 : : sequence_t pending_seq_consumer;
171 : : sequence_t pending_seq_cursor;
172 : :
173 : : sequence_t pending_seq_next;
174 : : sequence_t pending_seq_next_end;
175 : :
176 : : bool all_consumers_ignoring;
177 : :
178 : : Napi::Reference<Napi::Buffer<uint8_t>> shm_buffer_ref;
179 : : Napi::Reference<Napi::Buffer<uint8_t>> elements_buffer_ref;
180 : : Napi::Reference<Napi::Buffer<uint8_t>> consumers_buffer_ref;
181 : : Napi::FunctionReference slice_ref;
182 : :
183 : : Napi::Value GetConsumers(const Napi::CallbackInfo& info);
184 : : Napi::Value GetCursor(const Napi::CallbackInfo& info);
185 : : Napi::Value GetNext(const Napi::CallbackInfo& info);
186 : : Napi::Value GetElements(const Napi::CallbackInfo& info);
187 : : Napi::Value GetConsumer(const Napi::CallbackInfo& info);
188 : : Napi::Value GetPendingSeqConsumer(const Napi::CallbackInfo& info);
189 : : Napi::Value GetPendingSeqCursor(const Napi::CallbackInfo& info);
190 : : Napi::Value GetPendingSeqNext(const Napi::CallbackInfo& info);
191 : : Napi::Value GetPendingSeqNextEnd(const Napi::CallbackInfo& info);
192 : : Napi::Value GetAllConsumersIgnoring(const Napi::CallbackInfo& info);
193 : :
194 : : void ThrowErrnoError(const Napi::CallbackInfo& info,
195 : : const char *msg);
196 : : };
197 : :
198 : : //LCOV_EXCL_START
199 : : void NullCallback(const Napi::CallbackInfo&)
200 : : {
201 : : }
202 : : //LCOV_EXCL_STOP
203 : :
204 : 488217 : Napi::Function GetCallback(const Napi::CallbackInfo& info, const uint32_t cb_arg)
205 : : {
206 : 488217 : if (info.Length() > cb_arg)
207 : : {
208 : 488217 : Napi::Value cb = info[cb_arg];
209 : 488217 : if (cb.IsFunction())
210 : : {
211 : 488217 : return cb.As<Napi::Function>();
212 : : }
213 : : }
214 : :
215 : : return Napi::Function::New<&NullCallback>(info.Env()); //LCOV_EXCL_LINE
216 : : }
217 : :
218 : : class SyncBuffer
219 : : {
220 : : public:
221 : : typedef Napi::Buffer<uint8_t> Buffer;
222 : :
223 : 1582385 : static Buffer New(Napi::Env env, Disruptor *d, sequence_t start, sequence_t end)
224 : : {
225 : : #ifdef COVERAGE
226 : 4747155 : return d->slice_ref.Value().Call(d->elements_buffer_ref.Value(),
227 : : #else
228 : : return d->slice_ref.Call(d->elements_buffer_ref.Value(),
229 : : #endif
230 : : {
231 : 1582385 : Napi::Number::New(env, start * d->element_size),
232 : 3164770 : Napi::Number::New(env, end * d->element_size)
233 : 3164770 : }).As<Buffer>();
234 : : }
235 : : };
236 : :
237 : : class AsyncBuffer
238 : : {
239 : : public:
240 : : typedef AsyncBuffer Buffer;
241 : :
242 : 526365 : AsyncBuffer() : AsyncBuffer(0, 0, 0)
243 : : {
244 : 526365 : }
245 : :
246 : 526365 : static Buffer New(Napi::Env, Disruptor *d, sequence_t start, sequence_t end)
247 : : {
248 : 526365 : return Buffer(start, end, d->element_size);
249 : : }
250 : :
251 : 360539 : Napi::Value ToValue(Napi::Env env, Disruptor *d)
252 : : {
253 : 360539 : return SyncBuffer::New(env, d, start, end);
254 : : }
255 : :
256 : 170845 : size_t Length()
257 : : {
258 : 170845 : return length;
259 : : }
260 : :
261 : : private:
262 : 1052730 : AsyncBuffer(sequence_t start, sequence_t end, uint32_t element_size) :
263 : 1052730 : start(start),
264 : 1052730 : end(end),
265 : 1052730 : length((end - start) * element_size)
266 : : {
267 : 1052730 : }
268 : :
269 : : sequence_t start;
270 : : sequence_t end;
271 : : size_t length;
272 : : };
273 : :
274 : : template<typename T>
275 : : class AsyncArray
276 : : {
277 : : public:
278 : 2589014 : AsyncArray() :
279 : 2589014 : elements(std::make_unique<std::vector<T>>())
280 : : {
281 : 2589014 : }
282 : :
283 : 1294507 : static AsyncArray<T> New(Napi::Env)
284 : : {
285 : 1294507 : return AsyncArray<T>();
286 : : }
287 : :
288 : 355520 : void Set(uint32_t index, T&& el)
289 : : {
290 : 355520 : if (elements->size() <= index)
291 : : {
292 : 355520 : elements->resize(index + 1);
293 : : }
294 : :
295 : 355520 : (*elements)[index] = std::move(el);
296 : 355520 : }
297 : :
298 : 355475 : Napi::Value ToValue(Napi::Env env, Disruptor *d)
299 : : {
300 : 355475 : size_t length = elements->size();
301 : 355475 : Napi::Array r = Napi::Array::New(env);
302 : 710995 : for (size_t i = 0; i < length; ++i)
303 : : {
304 : 355520 : r[i] = (*elements)[i].ToValue(env, d);
305 : : }
306 : 355475 : return r;
307 : : }
308 : :
309 : 1294507 : size_t Length()
310 : : {
311 : 1294507 : return elements->size();
312 : : }
313 : :
314 : : private:
315 : : std::unique_ptr<std::vector<T>> elements;
316 : : };
317 : :
318 : : class AsyncBoolean
319 : : {
320 : : public:
321 : 2130801 : AsyncBoolean() :
322 : 2130801 : b(false)
323 : : {
324 : 2130801 : }
325 : :
326 : 2130801 : static AsyncBoolean New(Napi::Env, bool b)
327 : : {
328 : 2130801 : return AsyncBoolean(b);
329 : : }
330 : :
331 : 127723 : Napi::Value ToValue(Napi::Env env, Disruptor*)
332 : : {
333 : 127723 : return Napi::Boolean::New(env, b);
334 : : }
335 : :
336 : 2130801 : operator bool() const
337 : : {
338 : 2130801 : return b;
339 : : }
340 : :
341 : : private:
342 : 2130801 : AsyncBoolean(bool b) :
343 : 2130801 : b(b)
344 : : {
345 : 2130801 : }
346 : :
347 : : bool b;
348 : : };
349 : :
350 : : class AsyncUndefined
351 : : {
352 : : };
353 : :
354 : 370562 : Napi::Number ToValue(const Napi::Env& env, sequence_t n)
355 : : {
356 : 370562 : return Napi::Number::New(env, n);
357 : : }
358 : :
359 : 1094089 : Napi::Value ToValue(const Napi::Env& env, const AsyncUndefined&)
360 : : {
361 : 1094089 : return env.Undefined();
362 : : }
363 : :
364 : : template <typename Result,
365 : : typename Arg1 = AsyncUndefined,
366 : : typename Arg2 = AsyncUndefined,
367 : : typename Arg3 = AsyncUndefined>
368 : : class DisruptorAsyncWorker : public Napi::AsyncWorker
369 : : {
370 : : public:
371 : 3596153 : DisruptorAsyncWorker(Disruptor *disruptor,
372 : : const Napi::Function& callback) :
373 : : Napi::AsyncWorker(callback),
374 : 3596153 : retry(false),
375 : 3596153 : disruptor(disruptor), // disruptor_ref keeps this around
376 : 3596153 : disruptor_ref(Napi::Persistent(disruptor->Value()))
377 : : {
378 : 3596153 : }
379 : :
380 : : protected:
381 : : virtual void Retry() = 0;
382 : :
383 : 3596153 : void OnOK() override
384 : : {
385 : 3596153 : if (disruptor->Spin() && retry)
386 : : {
387 : 3107936 : return Retry();
388 : : }
389 : :
390 : 488217 : Napi::Env env = Env();
391 : :
392 : 3417519 : Callback().MakeCallback(
393 : 488217 : Receiver().Value(),
394 : : std::initializer_list<napi_value>
395 : : {
396 : 488217 : env.Null(),
397 : 488217 : result.ToValue(env, disruptor),
398 : 488217 : ToValue(env, arg1),
399 : 488217 : ToValue(env, arg2),
400 : 976434 : ToValue(env, arg3)
401 : : });
402 : : }
403 : :
404 : : Result result;
405 : : Arg1 arg1;
406 : : Arg2 arg2;
407 : : Arg3 arg3;
408 : : bool retry;
409 : : Disruptor *disruptor;
410 : :
411 : : private:
412 : : Napi::ObjectReference disruptor_ref;
413 : : };
414 : :
415 : : class CloseFD
416 : : {
417 : : public:
418 : 5763 : void operator()(int *fd)
419 : : {
420 : 5763 : close(*fd);
421 : 5763 : delete fd;
422 : 5763 : }
423 : : };
424 : :
425 : 8 : void Disruptor::ThrowErrnoError(const Napi::CallbackInfo& info,
426 : : const char *msg)
427 : : {
428 : 8 : int errnum = errno;
429 : 8 : char buf[1024] = {0};
430 : : #ifdef __APPLE__
431 : : auto err = strerror_r(errnum, buf, sizeof(buf));
432 : : static_assert(std::is_same<decltype(err), int>::value,
433 : : "strerror_r must return int");
434 : : char *errmsg = err == 0 ? buf : nullptr;
435 : : #else
436 : 8 : auto errmsg = strerror_r(errnum, buf, sizeof(buf));
437 : : static_assert(std::is_same<decltype(errmsg), char*>::value,
438 : : "strerror_r must return char*");
439 : : #endif
440 : 8 : throw Napi::Error::New(info.Env(),
441 : 16 : std::string(msg) + ": " + (errmsg ? errmsg : std::to_string(errnum)));
442 : : }
443 : :
444 : 5771 : Disruptor::Disruptor(const Napi::CallbackInfo& info) :
445 : : Napi::ObjectWrap<Disruptor>(info),
446 : 5771 : shm_buf(MAP_FAILED)
447 : : {
448 : : // Arguments
449 : 5771 : Napi::String shm_name = info[0].As<Napi::String>();
450 : 5771 : num_elements = info[1].As<Napi::Number>();
451 : 5771 : element_size = info[2].As<Napi::Number>();
452 : 5771 : num_consumers = info[3].As<Napi::Number>();
453 : 5771 : consumer = info[4].As<Napi::Number>();
454 : 5771 : init = info[5].As<Napi::Boolean>();
455 : 5771 : spin = info[6].As<Napi::Boolean>();
456 : :
457 : : // Open shared memory object
458 : : // OS X does not allow using O_TRUNC with shm_open.
459 : : // If this item exists, and init flag is true, delete it and recreate.
460 : 5771 : const auto name = shm_name.Utf8Value();
461 : 5771 : int shm_fd_tmp = shm_open(name.c_str(),
462 : 5771 : (init ? O_CREAT | O_EXCL : 0) | O_RDWR,
463 : : S_IRUSR | S_IWUSR);
464 : :
465 : 5771 : if (init && shm_fd_tmp < 0 && errno == EEXIST)
466 : : {
467 : 282 : shm_unlink(name.c_str());
468 : 282 : shm_fd_tmp = shm_open(name.c_str(),
469 : : O_CREAT | O_EXCL | O_RDWR,
470 : : S_IRUSR | S_IWUSR);
471 : : }
472 : :
473 : 5771 : if (shm_fd_tmp < 0)
474 : : {
475 : 8 : ThrowErrnoError(info, "Failed to open shared memory object");
476 : : }
477 : :
478 : 5763 : std::unique_ptr<int, CloseFD> shm_fd(new int(shm_fd_tmp));
479 : :
480 : : // Allow space for:
481 : : // - a sequence number for each consumer
482 : : // - the cursor sequence number (last filled slot)
483 : : // - the next sequence number (first free slot)
484 : : // - a status code
485 : : // - all the elements
486 : 5763 : shm_size = (num_consumers + 2) * sizeof(sequence_t) +
487 : 5763 : sizeof(status_t) +
488 : 5763 : num_elements * element_size;
489 : :
490 : : // Resize the shared memory if we're initializing it.
491 : : // Note: ftruncate initializes to null bytes.
492 : 5763 : if (init && (ftruncate(*shm_fd, shm_size) < 0))
493 : : {
494 : : ThrowErrnoError(info, "Failed to size shared memory"); //LCOV_EXCL_LINE
495 : : }
496 : :
497 : : // Map the shared memory
498 : 5763 : shm_buf = mmap(NULL,
499 : : shm_size,
500 : : PROT_READ | PROT_WRITE, MAP_SHARED,
501 : 5763 : *shm_fd,
502 : : 0);
503 : 5763 : if (shm_buf == MAP_FAILED)
504 : : {
505 : : ThrowErrnoError(info, "Failed to map shared memory"); //LCOV_EXCL_LINE
506 : : }
507 : :
508 : 5763 : consumers = static_cast<sequence_t*>(shm_buf);
509 : 5763 : cursor = &consumers[num_consumers];
510 : 5763 : next = &cursor[1];
511 : 5763 : status = reinterpret_cast<status_t*>(&next[1]);
512 : 5763 : elements = reinterpret_cast<uint8_t*>(&status[1]);
513 : 5763 : ptr_consumer = &consumers[consumer];
514 : :
515 : 5763 : pending_seq_consumer = 0;
516 : 5763 : pending_seq_cursor = 0;
517 : :
518 : 5763 : pending_seq_next = 1;
519 : 5763 : pending_seq_next_end = 0;
520 : :
521 : 5763 : all_consumers_ignoring = false;
522 : :
523 : : // From Node 14, V8 doesn't allow buffers pointing to the same memory:
524 : : //
525 : : // https://monorail-prod.appspot.com/p/v8/issues/detail?id=9908
526 : : // https://github.com/nodejs/node/issues/32463
527 : : //
528 : : // We work around this by having a single Buffer over all of shm_buf and using
529 : : // Buffer#slice() to return data from it.
530 : : //
531 : : // This leaves the issue of shm_buf getting the same address as a previous call -
532 : : // whether because the same shared memory is mapped or because it's been
533 : : // unmapped in Release() and then remapped again.
534 : : //
535 : : // Since Node 14.3.0, we can be sure that the finalizer is run after the memory
536 : : // pointer is removed from the BackingStore:
537 : : //
538 : : // https://github.com/nodejs/node/pull/33321
539 : : //
540 : : // However, if the memory is Release()d before it's removed from the BackingStore
541 : : // then mmap may return it again and we won't be able to pass it to V8 without
542 : : // it exiting.
543 : : //
544 : : // Further, even if we remember the Buffers we create in an unordered_map, they
545 : : // become invalid before the finalizer is called. The finalizer is only guaranteed
546 : : // to be called sometime after the object is collected:
547 : : //
548 : : // https://github.com/nodejs/node-addon-api/issues/702#issuecomment-625897608
549 : : //
550 : : // This also applies to weak N-API references to the object.
551 : : //
552 : : // We can detect this using napi_get_reference_value but there's nothing we can
553 : : // do about it since the value will still be in the BackingStore until the finalizer
554 : : // is run. It's this window of time that's the problem - between the Buffer being
555 : : // collected and the finalizer being called.
556 : : //
557 : : // The solution implemented below is to maintain an unordered set of shm_bufs that
558 : : // are still alive in the BackingStore. If mmap returns one of these, we search
559 : : // downwards for the next address not in the set, adjusting the length of the
560 : : // Buffer we need to create accordingly. Since we're slicing Buffer views over it,
561 : : // where it starts from doesn't matter.
562 : :
563 : 5763 : Napi::Env env = info.Env();
564 : 5763 : const auto JSBuffer = env.Global().Get("Buffer").As<Napi::Function>();
565 : 5763 : const auto proto = JSBuffer.Get("prototype").As<Napi::Object>();
566 : 5763 : slice_ref = Napi::Persistent(proto.Get("slice").As<Napi::Function>());
567 : :
568 : 5763 : auto shm_buf8 = static_cast<uint8_t*>(shm_buf);
569 : 5763 : auto shm_size8 = shm_size;
570 : 5763 : Napi::Buffer<uint8_t> shm_buffer;
571 : :
572 : : {
573 : 5763 : std::lock_guard<std::mutex> lock(buffers_mutex);
574 : :
575 : 9818 : while (shm_buf8) {
576 : 9818 : if (buffers->find(shm_buf8) == buffers->end()) {
577 : 5763 : break;
578 : : }
579 : 4055 : --shm_buf8;
580 : 4055 : ++shm_size8;
581 : : }
582 : 5763 : }
583 : :
584 : 5763 : if (!shm_buf8) {
585 : : //LCOV_EXCL_START
586 : : Release();
587 : : throw Napi::Error::New(env, "No space for buffer due to due to https://github.com/nodejs/node/issues/32463");
588 : : //LCOV_EXCL_STOP
589 : : }
590 : :
591 : 5763 : shm_buffer = Napi::Buffer<uint8_t>::New(
592 : : env,
593 : : shm_buf8,
594 : : shm_size8,
595 : 4758 : [](Napi::Env, uint8_t* shm_buf8) {
596 : 4758 : std::lock_guard<std::mutex> lock(buffers_mutex);
597 : 4758 : buffers->erase(shm_buf8);
598 : 4758 : });
599 : :
600 : : {
601 : 5763 : std::lock_guard<std::mutex> lock(buffers_mutex);
602 : 5763 : buffers->emplace(shm_buf8);
603 : 5763 : }
604 : :
605 : 5763 : shm_buffer_ref = Napi::Persistent(shm_buffer);
606 : :
607 : 5763 : const auto elements_start = elements - shm_buf8;
608 : 17289 : elements_buffer_ref = Napi::Persistent(slice_ref.Call(shm_buffer_ref.Value(),
609 : : {
610 : 5763 : Napi::Number::New(env, elements_start),
611 : 5763 : Napi::Number::New(env, elements_start + num_elements * element_size)
612 : 5763 : }).As<Napi::Buffer<uint8_t>>());
613 : :
614 : 5763 : const auto consumers_start = reinterpret_cast<uint8_t*>(consumers) - shm_buf8;
615 : 17289 : consumers_buffer_ref = Napi::Persistent(slice_ref.Call(shm_buffer_ref.Value(),
616 : : {
617 : 5763 : Napi::Number::New(env, consumers_start),
618 : 5763 : Napi::Number::New(env, consumers_start + num_consumers * sizeof(sequence_t))
619 : 5763 : }).As<Napi::Buffer<uint8_t>>());
620 : 5811 : }
621 : :
622 : 9516 : Disruptor::~Disruptor()
623 : : {
624 : 4758 : Release();
625 : 9516 : }
626 : :
627 : 10015 : int Disruptor::Release()
628 : : {
629 : 10015 : shm_buffer_ref.Reset();
630 : 10015 : elements_buffer_ref.Reset();
631 : 10015 : consumers_buffer_ref.Reset();
632 : 10015 : slice_ref.Reset();
633 : :
634 : 10015 : if (shm_buf != MAP_FAILED)
635 : : {
636 : 5762 : int r = munmap(shm_buf, shm_size);
637 : :
638 : 5762 : if (r < 0)
639 : : {
640 : : return r; //LCOV_EXCL_LINE
641 : : }
642 : :
643 : 5762 : shm_buf = MAP_FAILED;
644 : : }
645 : :
646 : 10015 : return 0;
647 : : }
648 : :
649 : 5257 : void Disruptor::Release(const Napi::CallbackInfo& info)
650 : : {
651 : 15771 : if ((shm_buf != MAP_FAILED) &&
652 : 6265 : (info.Length() >= 1) &&
653 : 6265 : info[0].As<Napi::Boolean>())
654 : : {
655 : 1008 : __atomic_store_n(ptr_consumer, sequence_max, memorder);
656 : : }
657 : :
658 : 5257 : if (Release() < 0)
659 : : {
660 : : ThrowErrnoError(info, "Failed to unmap shared memory"); //LCOV_EXCL_LINE
661 : : }
662 : 5257 : }
663 : :
664 : : #include <iostream>
665 : :
666 : : template<typename Array, typename DisruptorBuffer>
667 : 8149199 : Array Disruptor::ConsumeNewSync(const Napi::Env& env,
668 : : const bool retry,
669 : : sequence_t &start)
670 : : {
671 : : // Return all elements [&consumers[consumer], cursor)
672 : :
673 : : // Commit previous consume
674 : 8149199 : ConsumeCommit();
675 : :
676 : : do
677 : : {
678 : 8149199 : sequence_t seq_consumer = __atomic_load_n(ptr_consumer, memorder);
679 : 8149199 : sequence_t seq_cursor = __atomic_load_n(cursor, memorder);
680 : 8149199 : sequence_t pos_consumer = seq_consumer % num_elements;
681 : 8149199 : sequence_t pos_cursor = seq_cursor % num_elements;
682 : :
683 : 8149199 : if (pos_cursor > pos_consumer)
684 : : {
685 : 948060 : Array r = Array::New(env);
686 : 948060 : r.Set(0U, DisruptorBuffer::New(env, this, pos_consumer, pos_cursor));
687 : 948060 : UpdatePending(seq_consumer, seq_cursor);
688 : 948060 : start = seq_consumer;
689 : 948060 : return r;
690 : 355318 : }
691 : :
692 : 7201139 : if (seq_cursor != seq_consumer)
693 : : {
694 : 106157 : Array r = Array::New(env);
695 : 106157 : r.Set(0U, DisruptorBuffer::New(env, this, pos_consumer, num_elements));
696 : 106157 : if (pos_cursor > 0)
697 : : {
698 : 653 : r.Set(1U, DisruptorBuffer::New(env, this, 0, pos_cursor));
699 : : }
700 : 106157 : UpdatePending(seq_consumer, seq_cursor);
701 : 106157 : start = seq_consumer;
702 : 106157 : return r;
703 : 140 : }
704 : : }
705 : 7094982 : while (retry);
706 : :
707 : 7094982 : start = 0;
708 : : // ConsumeCommit() above already set pending_set_cursor to 0
709 : 7094982 : return Array::New(env);
710 : : }
711 : :
712 : 13 : Napi::Value Disruptor::ConsumeNewSync(const Napi::CallbackInfo& info)
713 : : {
714 : : sequence_t start;
715 : 13 : return ConsumeNewSync<Napi::Array, SyncBuffer>(info.Env(), spin, start);
716 : : }
717 : :
718 : : class ConsumeNewAsyncWorker :
719 : : public DisruptorAsyncWorker<AsyncArray<AsyncBuffer>, sequence_t>
720 : : {
721 : : public:
722 : 1074324 : ConsumeNewAsyncWorker(Disruptor *disruptor,
723 : 1074324 : const Napi::Function& callback) :
724 : : DisruptorAsyncWorker<AsyncArray<AsyncBuffer>, sequence_t>(
725 : 1074324 : disruptor, callback)
726 : : {
727 : 1074324 : arg1 = 0;
728 : 1074324 : }
729 : :
730 : : protected:
731 : 1074324 : void Execute() override
732 : : {
733 : : // Remember: don't access any V8 stuff in worker thread
734 : 1074324 : result = disruptor->ConsumeNewSync<AsyncArray<AsyncBuffer>, AsyncBuffer>(Env(), false, arg1);
735 : 1074324 : retry = result.Length() == 0;
736 : 1074324 : }
737 : :
738 : 718864 : void Retry() override
739 : : {
740 : 718864 : (new ConsumeNewAsyncWorker(disruptor, Callback().Value()))->Queue();
741 : 718864 : }
742 : : };
743 : :
744 : 355460 : void Disruptor::ConsumeNewAsync(const Napi::CallbackInfo& info)
745 : : {
746 : 355460 : (new ConsumeNewAsyncWorker(this, GetCallback(info, 0)))->Queue();
747 : 355460 : }
748 : :
749 : 7074862 : Napi::Value Disruptor::ConsumeNew(const Napi::CallbackInfo& info)
750 : : {
751 : : sequence_t start;
752 : 7074862 : Napi::Array r = ConsumeNewSync<Napi::Array, SyncBuffer>(
753 : 7074862 : info.Env(), false, start);
754 : :
755 : 7074862 : if ((r.Length() > 0) || !spin)
756 : : {
757 : 6719415 : return r;
758 : : }
759 : :
760 : 355447 : ConsumeNewAsync(info);
761 : 355447 : return info.Env().Undefined();
762 : : }
763 : :
764 : 7074880 : Napi::Value Disruptor::ConsumeCommit(const Napi::CallbackInfo& info)
765 : : {
766 : 7074880 : return Napi::Boolean::New(info.Env(), ConsumeCommit());
767 : : }
768 : :
769 : 1054217 : void Disruptor::UpdatePending(sequence_t seq_consumer, sequence_t seq_cursor)
770 : : {
771 : 1054217 : pending_seq_consumer = seq_consumer;
772 : 1054217 : pending_seq_cursor = seq_cursor;
773 : 1054217 : }
774 : :
775 : 15224079 : bool Disruptor::ConsumeCommit()
776 : : {
777 : 15224079 : bool r = true;
778 : :
779 : 15224079 : if (pending_seq_cursor)
780 : : {
781 : 1054216 : sequence_t expected = pending_seq_consumer;
782 : 1054216 : r = __atomic_compare_exchange_n(ptr_consumer,
783 : : &expected,
784 : : pending_seq_cursor,
785 : : false,
786 : : memorder,
787 : : memorder);
788 : 1054216 : pending_seq_cursor = 0;
789 : : }
790 : :
791 : 15224079 : return r;
792 : : }
793 : :
794 : : template<typename DisruptorBuffer>
795 : 691097 : typename DisruptorBuffer::Buffer Disruptor::ProduceClaimSync(const Napi::Env& env,
796 : : const bool retry,
797 : : sequence_t& out_next,
798 : : sequence_t& out_next_end,
799 : : bool& out_all_ignored)
800 : : {
801 : : bool all_ignored;
802 : :
803 : : do
804 : : {
805 : 691097 : sequence_t seq_next = __atomic_load_n(next, memorder);
806 : :
807 : 691097 : bool can_claim = true;
808 : 691097 : all_ignored = true;
809 : :
810 : 3236761 : for (uint32_t i = 0; i < num_consumers; ++i)
811 : : {
812 : 2711502 : sequence_t seq_consumer = __atomic_load_n(&consumers[i], memorder);
813 : :
814 : 2711502 : if (seq_consumer != sequence_max)
815 : : {
816 : 2711494 : all_ignored = false;
817 : :
818 : 2711494 : if ((seq_next - seq_consumer) >= num_elements)
819 : : {
820 : 165838 : can_claim = false;
821 : 165838 : break;
822 : : }
823 : : }
824 : : }
825 : :
826 : 691097 : if (all_ignored)
827 : : {
828 : 4 : break;
829 : : }
830 : :
831 : 1216348 : if (can_claim &&
832 : 525255 : __atomic_compare_exchange_n(next, &seq_next, seq_next + 1, false, memorder, memorder))
833 : : {
834 : 521039 : sequence_t start = seq_next % num_elements;
835 : 521039 : auto r = DisruptorBuffer::New(env, this, start, start + 1);
836 : 521039 : UpdateSeqNext(seq_next, seq_next, all_ignored);
837 : 521039 : out_next = seq_next;
838 : 521039 : out_next_end = seq_next;
839 : 521039 : out_all_ignored = all_ignored;
840 : 521039 : return r;
841 : : }
842 : : }
843 : 170054 : while (retry);
844 : :
845 : 170058 : UpdateSeqNext(1, 0, all_ignored);
846 : 170058 : out_next = 1;
847 : 170058 : out_next_end = 0;
848 : 170058 : out_all_ignored = all_ignored;
849 : 170058 : return DisruptorBuffer::New(env, this, 0, 0);
850 : : }
851 : :
852 : 807 : Napi::Value Disruptor::ProduceClaimSync(const Napi::CallbackInfo& info)
853 : : {
854 : : sequence_t seq_next, seq_next_end;
855 : : bool all_ignored;
856 : 807 : return ProduceClaimSync<SyncBuffer>(info.Env(), spin, seq_next, seq_next_end, all_ignored);
857 : : }
858 : :
859 : : class ProduceClaimAsyncWorker :
860 : : public DisruptorAsyncWorker<AsyncBuffer, sequence_t, sequence_t, bool>
861 : : {
862 : : public:
863 : 170845 : ProduceClaimAsyncWorker(Disruptor *disruptor,
864 : 170845 : const Napi::Function& callback) :
865 : : DisruptorAsyncWorker<AsyncBuffer, sequence_t, sequence_t, bool>(
866 : 170845 : disruptor, callback)
867 : : {
868 : 170845 : arg1 = 1;
869 : 170845 : arg2 = 0;
870 : 170845 : arg3 = false;
871 : 170845 : }
872 : :
873 : : protected:
874 : 170845 : void Execute() override
875 : : {
876 : : // Remember: don't access any V8 stuff in worker thread
877 : 170845 : result = disruptor->ProduceClaimSync<AsyncBuffer>(
878 : 170845 : Env(), false, arg1, arg2, arg3);
879 : 170845 : retry = result.Length() == 0;
880 : 170845 : }
881 : :
882 : 165826 : void Retry() override
883 : : {
884 : 165826 : (new ProduceClaimAsyncWorker(disruptor, Callback().Value()))->Queue();
885 : 165826 : }
886 : : };
887 : :
888 : 5019 : void Disruptor::ProduceClaimAsync(const Napi::CallbackInfo& info)
889 : : {
890 : 5019 : (new ProduceClaimAsyncWorker(this, GetCallback(info, 0)))->Queue();
891 : 5019 : }
892 : :
893 : 519445 : Napi::Value Disruptor::ProduceClaim(const Napi::CallbackInfo& info)
894 : : {
895 : : sequence_t seq_next, seq_next_end;
896 : : bool all_ignored;
897 : 519445 : Napi::Buffer<uint8_t> r = ProduceClaimSync<SyncBuffer>(
898 : 519445 : info.Env(), false, seq_next, seq_next_end, all_ignored);
899 : :
900 : 519445 : if ((r.Length() > 0) || all_ignored || !spin)
901 : : {
902 : 515233 : return r;
903 : : }
904 : :
905 : 4212 : ProduceClaimAsync(info);
906 : 4212 : return info.Env().Undefined();
907 : : }
908 : :
909 : : template<typename Array, typename DisruptorBuffer>
910 : 2236 : void Disruptor::ProduceGetBuffers(const Napi::Env& env,
911 : : const sequence_t seq_next,
912 : : const sequence_t seq_next_end,
913 : : const bool all_ignored,
914 : : Array& r)
915 : : {
916 : 2236 : sequence_t pos_next = seq_next % num_elements;
917 : 2236 : sequence_t pos_next_end = seq_next_end % num_elements;
918 : :
919 : 2236 : if (pos_next_end < pos_next)
920 : : {
921 : 8 : r.Set(0U, DisruptorBuffer::New(env, this, pos_next, num_elements));
922 : 8 : r.Set(1U, DisruptorBuffer::New(env, this, 0, pos_next_end + 1));
923 : : }
924 : : else
925 : : {
926 : 2228 : r.Set(0U, DisruptorBuffer::New(env, this, pos_next, pos_next_end + 1));
927 : : }
928 : :
929 : 2236 : UpdateSeqNext(seq_next, seq_next_end, all_ignored);
930 : 2236 : }
931 : :
932 : : template<typename Array, typename DisruptorBuffer>
933 : 147547 : Array Disruptor::ProduceClaimManySync(const Napi::Env& env,
934 : : const uint32_t n,
935 : : const bool retry,
936 : : sequence_t& out_next,
937 : : sequence_t& out_next_end,
938 : : bool& out_all_ignored)
939 : : {
940 : : bool all_ignored;
941 : :
942 : : do
943 : : {
944 : 147547 : sequence_t seq_next = __atomic_load_n(next, memorder);
945 : 147547 : sequence_t seq_next_end = seq_next + std::min(n, num_elements) - 1;
946 : :
947 : 147547 : bool can_claim = true;
948 : 147547 : all_ignored = true;
949 : :
950 : 147609 : for (uint32_t i = 0; i < num_consumers; ++i)
951 : : {
952 : 147563 : sequence_t seq_consumer = __atomic_load_n(&consumers[i], memorder);
953 : :
954 : 147563 : if (seq_consumer != sequence_max)
955 : : {
956 : 147551 : all_ignored = false;
957 : :
958 : 147551 : if ((seq_next_end - seq_consumer) >= num_elements)
959 : : {
960 : 147501 : can_claim = false;
961 : 147501 : break;
962 : : }
963 : : }
964 : : }
965 : :
966 : 147547 : if (all_ignored)
967 : : {
968 : 4 : break;
969 : : }
970 : :
971 : 147585 : if (can_claim &&
972 : 42 : __atomic_compare_exchange_n(next, &seq_next, seq_next_end + 1, false, memorder, memorder))
973 : : {
974 : 42 : Array r = Array::New(env);
975 : 42 : ProduceGetBuffers<Array, DisruptorBuffer>(env, seq_next, seq_next_end, all_ignored, r);
976 : 42 : out_next = seq_next;
977 : 42 : out_next_end = seq_next_end;
978 : 42 : out_all_ignored = all_ignored;
979 : 42 : return r;
980 : 9 : }
981 : : }
982 : 147501 : while (retry);
983 : :
984 : 147505 : UpdateSeqNext(1, 0, all_ignored);
985 : 147505 : out_next = 1;
986 : 147505 : out_next_end = 0;
987 : 147505 : out_all_ignored = all_ignored;
988 : 147505 : return Array::New(env);
989 : : }
990 : :
991 : 11 : Napi::Value Disruptor::ProduceClaimManySync(const Napi::CallbackInfo& info)
992 : : {
993 : : sequence_t seq_next, seq_next_end;
994 : : bool all_ignored;
995 : 22 : return ProduceClaimManySync<Napi::Array, SyncBuffer>(
996 : 33 : info.Env(), info[0].As<Napi::Number>(), spin, seq_next, seq_next_end, all_ignored);
997 : : }
998 : :
999 : : class ProduceClaimManyAsyncWorker :
1000 : : public DisruptorAsyncWorker<AsyncArray<AsyncBuffer>,
1001 : : sequence_t,
1002 : : sequence_t,
1003 : : bool>
1004 : : {
1005 : : public:
1006 : 147496 : ProduceClaimManyAsyncWorker(Disruptor *disruptor,
1007 : : const Napi::Function& callback,
1008 : 147496 : uint32_t n) :
1009 : : DisruptorAsyncWorker<AsyncArray<AsyncBuffer>,
1010 : : sequence_t,
1011 : : sequence_t,
1012 : : bool>(
1013 : : disruptor, callback),
1014 : 147496 : n(n)
1015 : : {
1016 : 147496 : arg1 = 1;
1017 : 147496 : arg2 = 0;
1018 : 147496 : arg3 = false;
1019 : 147496 : }
1020 : :
1021 : : protected:
1022 : 147496 : void Execute() override
1023 : : {
1024 : : // Remember: don't access any V8 stuff in worker thread
1025 : 294992 : result = disruptor->ProduceClaimManySync<AsyncArray<AsyncBuffer>, AsyncBuffer>(
1026 : 294992 : Env(), n, false, arg1, arg2, arg3);
1027 : 147496 : retry = result.Length() == 0;
1028 : 147496 : }
1029 : :
1030 : 147484 : void Retry() override
1031 : : {
1032 : 147484 : (new ProduceClaimManyAsyncWorker(disruptor, Callback().Value(), n))->Queue();
1033 : 147484 : }
1034 : :
1035 : : private:
1036 : : uint32_t n;
1037 : : };
1038 : :
1039 : 12 : void Disruptor::ProduceClaimManyAsync(const Napi::CallbackInfo& info,
1040 : : uint32_t n)
1041 : : {
1042 : 12 : (new ProduceClaimManyAsyncWorker(this, GetCallback(info, 1), n))->Queue();
1043 : 12 : }
1044 : :
1045 : 11 : void Disruptor::ProduceClaimManyAsync(const Napi::CallbackInfo& info)
1046 : : {
1047 : 11 : ProduceClaimManyAsync(info, info[0].As<Napi::Number>());
1048 : 11 : }
1049 : :
1050 : 40 : Napi::Value Disruptor::ProduceClaimMany(const Napi::CallbackInfo& info)
1051 : : {
1052 : 40 : uint32_t n = info[0].As<Napi::Number>();
1053 : : sequence_t seq_next, seq_next_end;
1054 : : bool all_ignored;
1055 : 40 : Napi::Array r = ProduceClaimManySync<Napi::Array, SyncBuffer>(
1056 : 40 : info.Env(), n, false, seq_next, seq_next_end, all_ignored);
1057 : :
1058 : 40 : if ((r.Length() > 0) || all_ignored || !spin)
1059 : : {
1060 : 39 : return r;
1061 : : }
1062 : :
1063 : 1 : ProduceClaimManyAsync(info, n);
1064 : 1 : return info.Env().Undefined();
1065 : : }
1066 : :
1067 : : template<typename Array, typename DisruptorBuffer>
1068 : 157001 : Array Disruptor::ProduceClaimAvailSync(const Napi::Env& env,
1069 : : const uint32_t max,
1070 : : const bool retry,
1071 : : sequence_t& out_next,
1072 : : sequence_t& out_next_end,
1073 : : bool& out_all_ignored)
1074 : : {
1075 : : bool all_ignored;
1076 : :
1077 : : do
1078 : : {
1079 : 157001 : sequence_t seq_next = __atomic_load_n(next, memorder);
1080 : 157001 : auto n = std::min(max, num_elements);
1081 : 157001 : all_ignored = true;
1082 : :
1083 : 420899 : for (uint32_t i = 0; i < num_consumers; ++i)
1084 : : {
1085 : 263898 : sequence_t seq_consumer = __atomic_load_n(&consumers[i], memorder);
1086 : 263898 : if (seq_consumer != sequence_max)
1087 : : {
1088 : 262890 : all_ignored = false;
1089 : 262890 : n = std::min(static_cast<sequence_t>(n), num_elements - (seq_next - seq_consumer));
1090 : : }
1091 : : }
1092 : :
1093 : 157001 : if (all_ignored)
1094 : : {
1095 : 5 : break;
1096 : : }
1097 : :
1098 : 159170 : if ((n > 0) &&
1099 : 2174 : __atomic_compare_exchange_n(next, &seq_next, seq_next + n, false, memorder, memorder))
1100 : : {
1101 : 2174 : Array r = Array::New(env);
1102 : 2174 : ProduceGetBuffers<Array, DisruptorBuffer>(env, seq_next, seq_next + n - 1, all_ignored, r);
1103 : 2174 : out_next = seq_next;
1104 : 2174 : out_next_end = seq_next + n - 1;
1105 : 2174 : out_all_ignored = all_ignored;
1106 : 2174 : return r;
1107 : 3 : }
1108 : : }
1109 : 154822 : while (retry);
1110 : :
1111 : 154827 : UpdateSeqNext(1, 0, all_ignored);
1112 : 154827 : out_next = 1;
1113 : 154827 : out_next_end = 0;
1114 : 154827 : out_all_ignored = all_ignored;
1115 : 154827 : return Array::New(env);
1116 : : }
1117 : :
1118 : 2 : Napi::Value Disruptor::ProduceClaimAvailSync(const Napi::CallbackInfo& info)
1119 : : {
1120 : : sequence_t seq_next, seq_next_end;
1121 : : bool all_ignored;
1122 : 4 : return ProduceClaimAvailSync<Napi::Array, SyncBuffer>(
1123 : 6 : info.Env(), info[0].As<Napi::Number>(), spin, seq_next, seq_next_end, all_ignored);
1124 : : }
1125 : :
1126 : : class ProduceClaimAvailAsyncWorker :
1127 : : public DisruptorAsyncWorker<AsyncArray<AsyncBuffer>,
1128 : : sequence_t,
1129 : : sequence_t,
1130 : : bool>
1131 : : {
1132 : : public:
1133 : 72687 : ProduceClaimAvailAsyncWorker(Disruptor *disruptor,
1134 : : const Napi::Function& callback,
1135 : 72687 : uint32_t max) :
1136 : : DisruptorAsyncWorker<AsyncArray<AsyncBuffer>,
1137 : : sequence_t,
1138 : : sequence_t,
1139 : : bool>(
1140 : : disruptor, callback),
1141 : 72687 : max(max)
1142 : : {
1143 : 72687 : arg1 = 1;
1144 : 72687 : arg2 = 0;
1145 : 72687 : arg3 = false;
1146 : 72687 : }
1147 : :
1148 : : protected:
1149 : 72687 : void Execute() override
1150 : : {
1151 : : // Remember: don't access any V8 stuff in worker thread
1152 : 145374 : result = disruptor->ProduceClaimAvailSync<AsyncArray<AsyncBuffer>, AsyncBuffer>(
1153 : 145374 : Env(), max, false, arg1, arg2, arg3);
1154 : 72687 : retry = result.Length() == 0;
1155 : 72687 : }
1156 : :
1157 : 72684 : void Retry() override
1158 : : {
1159 : 72684 : (new ProduceClaimAvailAsyncWorker(disruptor, Callback().Value(), max))->Queue();
1160 : 72684 : }
1161 : :
1162 : : private:
1163 : : uint32_t max;
1164 : : };
1165 : :
1166 : 3 : void Disruptor::ProduceClaimAvailAsync(const Napi::CallbackInfo& info,
1167 : : uint32_t max)
1168 : : {
1169 : 3 : (new ProduceClaimAvailAsyncWorker(this, GetCallback(info, 1), max))->Queue();
1170 : 3 : }
1171 : :
1172 : :
1173 : 2 : void Disruptor::ProduceClaimAvailAsync(const Napi::CallbackInfo& info)
1174 : : {
1175 : 2 : ProduceClaimAvailAsync(info, info[0].As<Napi::Number>());
1176 : 2 : }
1177 : :
1178 : 84312 : Napi::Value Disruptor::ProduceClaimAvail(const Napi::CallbackInfo& info)
1179 : : {
1180 : 84312 : uint32_t max = info[0].As<Napi::Number>();
1181 : : sequence_t seq_next, seq_next_end;
1182 : : bool all_ignored;
1183 : 84312 : Napi::Array r = ProduceClaimAvailSync<Napi::Array, SyncBuffer>(
1184 : 84312 : info.Env(), max, false, seq_next, seq_next_end, all_ignored);
1185 : :
1186 : 84312 : if ((r.Length() > 0) || all_ignored || !spin)
1187 : : {
1188 : 84311 : return r;
1189 : : }
1190 : :
1191 : 1 : ProduceClaimAvailAsync(info, max);
1192 : 1 : return info.Env().Undefined();
1193 : : }
1194 : :
1195 : 76 : Napi::Value Disruptor::ProduceRecover(const Napi::CallbackInfo& info)
1196 : : {
1197 : 76 : sequence_t seq_next = info[0].As<Napi::Number>().Int64Value();
1198 : 76 : sequence_t seq_next_end = info[1].As<Napi::Number>().Int64Value();
1199 : :
1200 : 76 : Napi::Array r = Napi::Array::New(info.Env());
1201 : :
1202 : 140 : if ((seq_next <= seq_next_end) &&
1203 : 116 : (__atomic_load_n(cursor, memorder) <= seq_next) &&
1204 : 40 : (__atomic_load_n(next, memorder) > seq_next_end))
1205 : : {
1206 : 20 : ProduceGetBuffers<Napi::Array, SyncBuffer>(
1207 : 40 : info.Env(), seq_next, seq_next_end, false, r);
1208 : : }
1209 : :
1210 : 76 : return r;
1211 : : }
1212 : :
1213 : : template<typename Boolean>
1214 : 2653230 : Boolean Disruptor::ProduceCommitSync(const Napi::Env& env,
1215 : : sequence_t seq_next,
1216 : : sequence_t seq_next_end,
1217 : : const bool retry)
1218 : : {
1219 : 2653230 : if (seq_next <= seq_next_end)
1220 : : {
1221 : : do
1222 : : {
1223 : 2653222 : sequence_t expected = seq_next;
1224 : 2653222 : if (__atomic_compare_exchange_n(cursor, &expected, seq_next_end + 1, false, memorder, memorder))
1225 : : {
1226 : 523228 : return Boolean::New(env, true);
1227 : : }
1228 : : }
1229 : 2129994 : while (retry);
1230 : : }
1231 : :
1232 : 2130002 : return Boolean::New(env, false);
1233 : : }
1234 : :
1235 : 995665 : void Disruptor::UpdateSeqNext(const sequence_t seq_next,
1236 : : const sequence_t seq_next_end,
1237 : : const bool all_ignored)
1238 : : {
1239 : 995665 : pending_seq_next = seq_next;
1240 : 995665 : pending_seq_next_end = seq_next_end;
1241 : 995665 : all_consumers_ignoring = all_ignored;
1242 : 995665 : }
1243 : :
1244 : 523240 : uint32_t Disruptor::GetSeqNext(const Napi::CallbackInfo& info,
1245 : : sequence_t& seq_next,
1246 : : sequence_t& seq_next_end)
1247 : : {
1248 : 523240 : if (info.Length() >= 2)
1249 : : {
1250 : 2177 : seq_next = info[0].As<Napi::Number>().Int64Value();
1251 : 2177 : seq_next_end = info[1].As<Napi::Number>().Int64Value();
1252 : 2177 : return 2;
1253 : : }
1254 : :
1255 : 521063 : seq_next = pending_seq_next;
1256 : 521063 : seq_next_end = pending_seq_next_end;
1257 : 521063 : return 0;
1258 : : }
1259 : :
1260 : 811 : Napi::Value Disruptor::ProduceCommitSync(const Napi::CallbackInfo& info)
1261 : : {
1262 : : sequence_t seq_next, seq_next_end;
1263 : 811 : GetSeqNext(info, seq_next, seq_next_end);
1264 : 811 : return ProduceCommitSync<Napi::Boolean>(info.Env(), seq_next, seq_next_end, spin);
1265 : : }
1266 : :
1267 : : class ProduceCommitAsyncWorker :
1268 : : public DisruptorAsyncWorker<AsyncBoolean>
1269 : : {
1270 : : public:
1271 : 2130801 : ProduceCommitAsyncWorker(Disruptor *disruptor,
1272 : : const Napi::Function& callback,
1273 : : sequence_t seq_next,
1274 : 2130801 : sequence_t seq_next_end) :
1275 : : DisruptorAsyncWorker<AsyncBoolean>(disruptor, callback),
1276 : 2130801 : seq_next(seq_next),
1277 : 2130801 : seq_next_end(seq_next_end)
1278 : : {
1279 : 2130801 : }
1280 : :
1281 : : protected:
1282 : 2130801 : void Execute() override
1283 : : {
1284 : : // Remember: don't access any V8 stuff in worker thread
1285 : 2130801 : result = disruptor->ProduceCommitSync<AsyncBoolean>(Env(), seq_next, seq_next_end, false);
1286 : 2130801 : retry = !result;
1287 : 2130801 : }
1288 : :
1289 : 2003078 : void Retry() override
1290 : : {
1291 : 2003078 : (new ProduceCommitAsyncWorker(disruptor, Callback().Value(), seq_next, seq_next_end))->Queue();
1292 : 2003078 : }
1293 : :
1294 : : private:
1295 : : sequence_t seq_next, seq_next_end;
1296 : : };
1297 : :
1298 : 127723 : void Disruptor::ProduceCommitAsync(const Napi::CallbackInfo& info,
1299 : : sequence_t seq_next,
1300 : : sequence_t seq_next_end,
1301 : : uint32_t cb_arg)
1302 : : {
1303 : : (new ProduceCommitAsyncWorker(
1304 : 127723 : this, GetCallback(info, cb_arg), seq_next, seq_next_end))->Queue();
1305 : 127723 : }
1306 : :
1307 : 811 : void Disruptor::ProduceCommitAsync(const Napi::CallbackInfo& info)
1308 : : {
1309 : : sequence_t seq_next, seq_next_end;
1310 : 811 : uint32_t cb_arg = GetSeqNext(info, seq_next, seq_next_end);
1311 : 811 : ProduceCommitAsync(info, seq_next, seq_next_end, cb_arg);
1312 : 811 : }
1313 : :
1314 : 521618 : Napi::Value Disruptor::ProduceCommit(const Napi::CallbackInfo& info)
1315 : : {
1316 : : sequence_t seq_next, seq_next_end;
1317 : 521618 : uint32_t cb_arg = GetSeqNext(info, seq_next, seq_next_end);
1318 : :
1319 : 521618 : Napi::Boolean r = ProduceCommitSync<Napi::Boolean>(info.Env(), seq_next, seq_next_end, false);
1320 : 521618 : if (r || !spin)
1321 : : {
1322 : 394706 : return r;
1323 : : }
1324 : :
1325 : 126912 : ProduceCommitAsync(info, seq_next, seq_next_end, cb_arg);
1326 : 126912 : return info.Env().Undefined();
1327 : : }
1328 : :
1329 : 12 : Napi::Value Disruptor::GetConsumers(const Napi::CallbackInfo&)
1330 : : {
1331 : 12 : return consumers_buffer_ref.Value();
1332 : : }
1333 : :
1334 : 222 : Napi::Value Disruptor::GetCursor(const Napi::CallbackInfo& info)
1335 : : {
1336 : 222 : return Napi::Number::New(info.Env(), __atomic_load_n(cursor, memorder));
1337 : : }
1338 : :
1339 : 222 : Napi::Value Disruptor::GetNext(const Napi::CallbackInfo& info)
1340 : : {
1341 : 222 : return Napi::Number::New(info.Env(), __atomic_load_n(next, memorder));
1342 : : }
1343 : :
1344 : 1556 : Napi::Value Disruptor::GetElements(const Napi::CallbackInfo&)
1345 : : {
1346 : 1556 : return elements_buffer_ref.Value();
1347 : : }
1348 : :
1349 : 222 : Napi::Value Disruptor::GetConsumer(const Napi::CallbackInfo& info)
1350 : : {
1351 : 222 : return Napi::Number::New(info.Env(), __atomic_load_n(ptr_consumer, memorder));
1352 : : }
1353 : :
1354 : 7074962 : Napi::Value Disruptor::GetPendingSeqConsumer(const Napi::CallbackInfo& info)
1355 : : {
1356 : 7074962 : return Napi::Number::New(info.Env(), pending_seq_consumer);
1357 : : }
1358 : :
1359 : 87 : Napi::Value Disruptor::GetPendingSeqCursor(const Napi::CallbackInfo& info)
1360 : : {
1361 : 87 : return Napi::Number::New(info.Env(), pending_seq_cursor);
1362 : : }
1363 : :
1364 : 604663 : Napi::Value Disruptor::GetPendingSeqNext(const Napi::CallbackInfo& info)
1365 : : {
1366 : 604663 : return Napi::Number::New(info.Env(), pending_seq_next);
1367 : : }
1368 : :
1369 : 604663 : Napi::Value Disruptor::GetPendingSeqNextEnd(const Napi::CallbackInfo& info)
1370 : : {
1371 : 604663 : return Napi::Number::New(info.Env(), pending_seq_next_end);
1372 : : }
1373 : :
1374 : 604649 : Napi::Value Disruptor::GetAllConsumersIgnoring(const Napi::CallbackInfo& info)
1375 : : {
1376 : 604649 : return Napi::Boolean::New(info.Env(), all_consumers_ignoring);
1377 : : }
1378 : :
1379 : 2104 : Napi::Value Disruptor::GetElementSize(const Napi::CallbackInfo& info)
1380 : : {
1381 : 2104 : return Napi::Number::New(info.Env(), element_size);
1382 : : }
1383 : :
1384 : 2026 : Napi::Value Disruptor::GetSpin(const Napi::CallbackInfo& info)
1385 : : {
1386 : 2026 : return Napi::Boolean::New(info.Env(), spin);
1387 : : }
1388 : :
1389 : 12036314 : Napi::Value Disruptor::GetStatus(const Napi::CallbackInfo& info)
1390 : : {
1391 : 12036314 : return Napi::Number::New(info.Env(), __atomic_load_n(status, memorder));
1392 : : }
1393 : :
1394 : 11 : void Disruptor::SetStatus(const Napi::CallbackInfo&, const Napi::Value& value)
1395 : : {
1396 : 11 : __atomic_store_n(status, value.As<Napi::Number>(), memorder);
1397 : 11 : }
1398 : :
1399 : 506 : Napi::Object Disruptor::Initialize(Napi::Env env, Napi::Object exports)
1400 : : {
1401 : : {
1402 : 506 : std::lock_guard<std::mutex> lock(buffers_mutex);
1403 : 506 : if (!buffers) {
1404 : 254 : buffers = new std::unordered_set<uint8_t*>();
1405 : : }
1406 : 506 : }
1407 : :
1408 : 506 : exports.Set("Disruptor", DefineClass(env, "Disruptor",
1409 : : {
1410 : 506 : InstanceMethod<&Disruptor::ProduceClaim>("produceClaim"),
1411 : 506 : InstanceMethod<&Disruptor::ProduceClaimSync>("produceClaimSync"),
1412 : 506 : InstanceMethod<&Disruptor::ProduceClaimMany>("produceClaimMany"),
1413 : 506 : InstanceMethod<&Disruptor::ProduceClaimManySync>("produceClaimManySync"),
1414 : 506 : InstanceMethod<&Disruptor::ProduceClaimAvail>("produceClaimAvail"),
1415 : 506 : InstanceMethod<&Disruptor::ProduceClaimAvailSync>("produceClaimAvailSync"),
1416 : 506 : InstanceMethod<&Disruptor::ProduceCommit>("produceCommit"),
1417 : 506 : InstanceMethod<&Disruptor::ProduceCommitSync>("produceCommitSync"),
1418 : 506 : InstanceMethod<&Disruptor::ProduceRecover>("produceRecover"),
1419 : 506 : InstanceMethod<&Disruptor::ConsumeNew>("consumeNew"),
1420 : 506 : InstanceMethod<&Disruptor::ConsumeNewSync>("consumeNewSync"),
1421 : 506 : InstanceMethod<&Disruptor::ConsumeCommit>("consumeCommit"),
1422 : 506 : InstanceMethod<&Disruptor::Release>("release"),
1423 : 506 : InstanceAccessor<&Disruptor::GetPendingSeqConsumer>("prevConsumeStart"),
1424 : 506 : InstanceAccessor<&Disruptor::GetPendingSeqNext>("prevClaimStart"),
1425 : 506 : InstanceAccessor<&Disruptor::GetPendingSeqNextEnd>("prevClaimEnd"),
1426 : 506 : InstanceAccessor<&Disruptor::GetAllConsumersIgnoring>("allConsumersIgnoring"),
1427 : 506 : InstanceAccessor<&Disruptor::GetElementSize>("elementSize"),
1428 : 506 : InstanceAccessor<&Disruptor::GetSpin>("spin"),
1429 : 506 : InstanceAccessor<&Disruptor::GetStatus, &Disruptor::SetStatus>("status"),
1430 : :
1431 : : // For testing only
1432 : 506 : InstanceAccessor<&Disruptor::GetConsumers>("consumers"),
1433 : 506 : InstanceAccessor<&Disruptor::GetCursor>("cursor"),
1434 : 506 : InstanceAccessor<&Disruptor::GetNext>("next"),
1435 : 506 : InstanceAccessor<&Disruptor::GetElements>("elements"),
1436 : 506 : InstanceAccessor<&Disruptor::GetConsumer>("consumer"),
1437 : 506 : InstanceAccessor<&Disruptor::GetPendingSeqCursor>("prevConsumeNext"),
1438 : 506 : InstanceMethod<&Disruptor::ConsumeNewAsync>("consumeNewAsync"),
1439 : 506 : InstanceMethod<&Disruptor::ProduceClaimAsync>("produceClaimAsync"),
1440 : 506 : InstanceMethod<&Disruptor::ProduceClaimManyAsync>("produceClaimManyAsync"),
1441 : 506 : InstanceMethod<&Disruptor::ProduceClaimAvailAsync>("produceClaimAvailAsync"),
1442 : 506 : InstanceMethod<&Disruptor::ProduceCommitAsync>("produceCommitAsync")
1443 : : }));
1444 : :
1445 : 506 : return exports;
1446 : : }
1447 : :
1448 : 506 : Napi::Object Initialize(Napi::Env env, Napi::Object exports)
1449 : : {
1450 : 506 : return Disruptor::Initialize(env, exports);
1451 : : }
1452 : :
1453 : 760 : NODE_API_MODULE(disruptor, Initialize)
|