Skip to content
File

Blob: src/workerd/io/io-own.c++

4.3 KB
1#include "io-own.h"
2 
3#include "io-context.h"
4 
5#include <workerd/jsg/util.h>
6 
7namespace workerd {
8 
9void DeleteQueue::scheduleDeletion(OwnedObject* object) const {
10 if (IoContext::hasCurrent() && IoContext::current().deleteQueue.queue.get() == this) {
11 // Deletion from same thread. No need to enqueue.
12 kj::AllowAsyncDestructorsScope scope;
13 OwnedObjectList::unlink(*object);
14 } else {
15 auto lock = crossThreadDeleteQueue.lockExclusive();
16 KJ_IF_SOME(state, *lock) {
17 state.queue.add(object);
18 }
19 }
20}
21 
22void DeleteQueue::scheduleAction(jsg::Lock& js, kj::Function<void(jsg::Lock&)>&& action) const {
23 {
24 auto lock = crossThreadDeleteQueue.lockExclusive();
25 KJ_IF_SOME(state, *lock) {
26 state.actions.add(kj::mv(action));
27 KJ_REQUIRE_NONNULL(state.crossThreadFulfiller)->fulfill();
28 return;
29 }
30 }
31 
32 // The queue was deleted, likely because the IoContext was destroyed and the
33 // DeleteQueuePtr was invalidated. We are going to emit a warning and drop the
34 // actions on the floor without scheduling them.
35 KJ_IF_SOME(ioContext, IoContext::tryCurrent()) {
36 // We are creating an error here just so we can include the JavaScript stack
37 // with the warning if it exists. We are not going to throw this error.
38 auto err = v8::Exception::Error(
39 js.str("A promise was resolved or rejected from a different request context than "
40 "the one it was created in. However, the creating request has already been "
41 "completed or canceled. Continuations for that request are unlikely to "
42 "run safely and have been canceled. If this behavior breaks your worker, "
43 "consider setting the `no_handle_cross_request_promise_resolution` "
44 "compatibility flag for your worker."_kj))
45 .As<v8::Object>();
46 // TODO(soon): Add documentation link to this warning.
47 // Changing the name property to "Warning" will make the serialize stack start with
48 // "Warning: " rather that "Error: "
49 jsg::check(err->Set(js.v8Context(), js.str("name"_kj), js.str("Warning"_kj)));
50 auto stack = jsg::check(err->Get(js.v8Context(), js.str("stack"_kj)));
51 
52 // Safe to mutate here since we have the exclusive lock on the queue above.
53 ioContext.logWarning(kj::str(stack));
54 }
55}
56 
57void DeleteQueue::checkFarGet(const DeleteQueue& deleteQueue, const std::type_info& type) {
58 IoContext::current().checkFarGet(deleteQueue, type);
59}
60 
61void DeleteQueue::checkWeakGet(workerd::WeakRef<IoContext>& weak) {
62 JSG_REQUIRE(weak.isValid(), Error,
63 "Couldn't complete operation because the execution context has ended.");
64}
65 
66kj::Promise<void> DeleteQueue::resetCrossThreadSignal() const {
67 auto lock = crossThreadDeleteQueue.lockExclusive();
68 KJ_IF_SOME(state, *lock) {
69 KJ_IF_SOME(fulfiller, state.crossThreadFulfiller) {
70 // We should only reset the signal if it has been fulfilled.
71 KJ_ASSERT(!fulfiller->isWaiting());
72 }
73 auto paf = kj::newPromiseAndCrossThreadFulfiller<void>();
74 state.crossThreadFulfiller = kj::mv(paf.fulfiller);
75 return kj::mv(paf.promise);
76 } else {
77 return kj::NEVER_DONE;
78 }
79}
80 
81OwnedObjectList::~OwnedObjectList() noexcept(false) {
82 while (head != kj::none) {
83 // We want to have the same order of operations as the recursive destructor here. Without this
84 // optimization, `~SpecificOwnedObject<T>` is invoked first, then `~OwnedObject()` which
85 // destructs the next node which continues the process. The key takeaway is that we destroy each
86 // node's `T` before we move onto the next node. This duplicates that behavior by unlinking
87 // forward through the list, which hopefully should keep our stack size low no matter how many
88 // `OwnedObject` instances we have.
89 unlink(*KJ_ASSERT_NONNULL(head));
90 }
91}
92 
93void OwnedObjectList::unlink(OwnedObject& object) {
94 KJ_IF_SOME(next, object.next) {
95 next.get()->prev = object.prev;
96 }
97 *object.prev = kj::mv(object.next);
98}
99 
100void OwnedObjectList::link(kj::Own<OwnedObject> object) {
101 object->next = kj::mv(head);
102 KJ_IF_SOME(next, object->next) {
103 next.get()->prev = &object->next;
104 }
105 object->prev = &head;
106 head = kj::mv(object);
107}
108 
109void IoCrossContextExecutor::execute(jsg::Lock& js, kj::Function<void(jsg::Lock&)>&& func) {
110 deleteQueue->scheduleAction(js, kj::mv(func));
111}
112 
113} // namespace workerd