Skip to content

Commit

Permalink
Support signal option on iterators
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers committed Feb 3, 2024
1 parent 551b5f7 commit b064215
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
24 changes: 23 additions & 1 deletion binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ struct Iterator final : public BaseIterator {
first_(true),
nexting_(false),
isClosing_(false),
aborted_(false),
ended_(false),
ref_(NULL) {
}
Expand All @@ -959,7 +960,7 @@ struct Iterator final : public BaseIterator {
size_t bytesRead = 0;
leveldb::Slice empty;

while (true) {
while (!aborted_) {
if (!first_) Next();
else first_ = false;

Expand Down Expand Up @@ -998,6 +999,7 @@ struct Iterator final : public BaseIterator {
bool first_;
bool nexting_;
bool isClosing_;
bool aborted_;
bool ended_;
std::vector<Entry> cache_;

Expand Down Expand Up @@ -1805,6 +1807,16 @@ NAPI_METHOD(iterator_close) {
return promise;
}

/**
* Aborts a NextWorker (if any, eventually).
*/
NAPI_METHOD(iterator_abort) {
NAPI_ARGV(1);
NAPI_ITERATOR_CONTEXT();
iterator->aborted_ = true;
NAPI_RETURN_UNDEFINED();
}

/**
* Worker class for nexting an iterator.
*/
Expand All @@ -1828,6 +1840,15 @@ struct NextWorker final : public BaseWorker {
}

void HandleOKCallback (napi_env env, napi_deferred deferred) override {
if (iterator_->aborted_) {
napi_value err = CreateCodeError(env, "LEVEL_ABORTED", "Operation has been aborted");
napi_value name;
napi_create_string_utf8(env, "AbortError", NAPI_AUTO_LENGTH, &name);
napi_set_named_property(env, err, "name", name);
napi_reject_deferred(env, deferred, err);
return;
}

size_t size = iterator_->cache_.size();
napi_value jsArray;
napi_create_array_with_length(env, size, &jsArray);
Expand Down Expand Up @@ -2154,6 +2175,7 @@ NAPI_INIT() {
NAPI_EXPORT_FUNCTION(iterator_seek);
NAPI_EXPORT_FUNCTION(iterator_close);
NAPI_EXPORT_FUNCTION(iterator_nextv);
NAPI_EXPORT_FUNCTION(iterator_abort);

NAPI_EXPORT_FUNCTION(batch_do);
NAPI_EXPORT_FUNCTION(batch_init);
Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class ClassicLevel extends AbstractLevel {
additionalMethods: {
approximateSize: true,
compactRange: true
},
signals: {
iterators: true
}
}, options)

Expand Down
22 changes: 22 additions & 0 deletions iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const kContext = Symbol('context')
const kCache = Symbol('cache')
const kFirst = Symbol('first')
const kPosition = Symbol('position')
const kSignal = Symbol('signal')
const kAbort = Symbol('abort')
const empty = []

// Does not implement _all() because the default implementation
Expand All @@ -22,6 +24,15 @@ class Iterator extends AbstractIterator {
this[kFirst] = true
this[kCache] = empty
this[kPosition] = 0
this[kAbort] = this[kAbort].bind(this)

// TODO: consider exposing iterator.signal in abstract-level
if (options.signal != null) {
this[kSignal] = options.signal
this[kSignal].addEventListener('abort', this[kAbort], { once: true })
} else {
this[kSignal] = null
}
}

_seek (target, options) {
Expand Down Expand Up @@ -63,9 +74,20 @@ class Iterator extends AbstractIterator {

async _close () {
this[kCache] = empty

if (this[kSignal] !== null) {
this[kSignal].removeEventListener('abort', this[kAbort])
this[kSignal] = null
}

return binding.iterator_close(this[kContext])
}

[kAbort] () {
this[kSignal] = null
binding.iterator_abort(this[kContext])
}

// Undocumented, exposed for tests only
get cached () {
return this[kCache].length - this[kPosition]
Expand Down

0 comments on commit b064215

Please sign in to comment.