Skip to content

Commit 6e196dc

Browse files
committed
Support signal option on iterators
This is a new optional feature in `abstract-level` 2.0.0. Category: addition
1 parent 50e03dc commit 6e196dc

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

binding.cc

+24-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <map>
1313
#include <vector>
1414
#include <mutex>
15+
#include <atomic>
1516

1617
/**
1718
* Forward declarations.
@@ -943,6 +944,7 @@ struct Iterator final : public BaseIterator {
943944
first_(true),
944945
nexting_(false),
945946
isClosing_(false),
947+
aborted_(false),
946948
ended_(false),
947949
state_(state),
948950
ref_(NULL) {
@@ -966,7 +968,7 @@ struct Iterator final : public BaseIterator {
966968
size_t bytesRead = 0;
967969
leveldb::Slice empty;
968970

969-
while (true) {
971+
while (!aborted_) {
970972
if (!first_) Next();
971973
else first_ = false;
972974

@@ -1005,6 +1007,7 @@ struct Iterator final : public BaseIterator {
10051007
bool first_;
10061008
bool nexting_;
10071009
bool isClosing_;
1010+
std::atomic<bool> aborted_;
10081011
bool ended_;
10091012
unsigned char* state_;
10101013
std::vector<Entry> cache_;
@@ -1819,6 +1822,16 @@ NAPI_METHOD(iterator_close) {
18191822
return promise;
18201823
}
18211824

1825+
/**
1826+
* Aborts a NextWorker (if any, eventually).
1827+
*/
1828+
NAPI_METHOD(iterator_abort) {
1829+
NAPI_ARGV(1);
1830+
NAPI_ITERATOR_CONTEXT();
1831+
iterator->aborted_ = true;
1832+
NAPI_RETURN_UNDEFINED();
1833+
}
1834+
18221835
/**
18231836
* Worker class for nexting an iterator.
18241837
*/
@@ -1842,6 +1855,15 @@ struct NextWorker final : public BaseWorker {
18421855
}
18431856

18441857
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1858+
if (iterator_->aborted_) {
1859+
napi_value err = CreateCodeError(env, "LEVEL_ABORTED", "Operation has been aborted");
1860+
napi_value name;
1861+
napi_create_string_utf8(env, "AbortError", NAPI_AUTO_LENGTH, &name);
1862+
napi_set_named_property(env, err, "name", name);
1863+
napi_reject_deferred(env, deferred, err);
1864+
return;
1865+
}
1866+
18451867
size_t size = iterator_->cache_.size();
18461868
napi_value jsArray;
18471869
napi_create_array_with_length(env, size, &jsArray);
@@ -2173,6 +2195,7 @@ NAPI_INIT() {
21732195
NAPI_EXPORT_FUNCTION(iterator_seek);
21742196
NAPI_EXPORT_FUNCTION(iterator_close);
21752197
NAPI_EXPORT_FUNCTION(iterator_nextv);
2198+
NAPI_EXPORT_FUNCTION(iterator_abort);
21762199

21772200
NAPI_EXPORT_FUNCTION(batch_do);
21782201
NAPI_EXPORT_FUNCTION(batch_init);

index.js

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class ClassicLevel extends AbstractLevel {
2828
additionalMethods: {
2929
approximateSize: true,
3030
compactRange: true
31+
},
32+
signals: {
33+
iterators: true
3134
}
3235
}, options)
3336

iterator.js

+20
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ class Iterator extends AbstractIterator {
2929
this[kFirst] = true
3030
this[kCache] = empty
3131
this[kPosition] = 0
32+
this[kAbort] = this[kAbort].bind(this)
33+
34+
// TODO: consider exposing iterator.signal in abstract-level
35+
if (options.signal != null) {
36+
this[kSignal] = options.signal
37+
this[kSignal].addEventListener('abort', this[kAbort], { once: true })
38+
} else {
39+
this[kSignal] = null
40+
}
3241
}
3342

3443
_seek (target, options) {
@@ -81,9 +90,20 @@ class Iterator extends AbstractIterator {
8190

8291
async _close () {
8392
this[kCache] = empty
93+
94+
if (this[kSignal] !== null) {
95+
this[kSignal].removeEventListener('abort', this[kAbort])
96+
this[kSignal] = null
97+
}
98+
8499
return binding.iterator_close(this[kContext])
85100
}
86101

102+
[kAbort] () {
103+
this[kSignal] = null
104+
binding.iterator_abort(this[kContext])
105+
}
106+
87107
// Undocumented, exposed for tests only
88108
get cached () {
89109
return this[kCache].length - this[kPosition]

0 commit comments

Comments
 (0)