Files
sdk/runtime/vm/message_handler.h
T
Daco Harkes 37526fc8d7 Revert "[vm] Implement Finalizer"
This reverts commit 7dca34c235.

Reason for revert: b/226085355 dart_vm_test crashing. Unclear what
the cause is. Reverting so we can triage the issue.

TEST=This is a revert.

Original change's description:
> [vm] Implement `Finalizer`
>
> This CL implements the `Finalizer` in the GC.
>
> (This CL does not yet implement `NativeFinalizer`.)
>
> The GC is specially aware of two types of objects for the purposes of
> running finalizers.
>
> 1) `FinalizerEntry`
> 2) `Finalizer` (`FinalizerBase`, `_FinalizerImpl`)
>
> A `FinalizerEntry` contains the `value`, the optional `detach` key, and
> the `token`, and a reference to the `finalizer`.
> An entry only holds on weakly to the value, detach key, and finalizer.
> (Similar to how `WeakReference` only holds on weakly to target).
>
> A `Finalizer` contains all entries, a list of entries of which the value
> is collected, and a reference to the isolate.
>
> When a the value of an entry is GCed, the enry is added over to the
> collected list.
> If any entry is moved to the collected list, a message is sent that
> invokes the finalizer to call the callback on all entries in that list.
>
> When a finalizer is detached by the user, the entry token is set to the
> entry itself and is removed from the all entries set.
> This ensures that if the entry was already moved to the collected list,
> the finalizer is not executed.
>
> To speed up detaching, we use a weak map from detach keys to list of
> entries. This ensures entries can be GCed.
>
> Both the scavenger and marker tasks process finalizer entries in
> parallel.
> Parallel tasks use an atomic exchange on the head of the collected
> entries list, ensuring no entries get lost.
> The mutator thread is guaranteed to be stopped when processing entries.
> This ensures that we do not need barriers for moving entries into the
> finalizers collected list.
> Dart reads and replaces the collected entries list also with an atomic
> exchange, ensuring the GC doesn't run in between a load/store.
>
> When a finalizer gets posted a message to process finalized objects, it
> is being kept alive by the message.
> An alternative design would be to pre-allocate a `WeakReference` in the
> finalizer pointing to the finalizer, and send that itself.
> This would be at the cost of an extra object.
>
> Send and exit is not supported in this CL, support will be added in a
> follow up CL. Trying to send will throw.
>
> Bug: https://github.com/dart-lang/sdk/issues/47777
>
> TEST=runtime/tests/vm/dart/finalizer/*
> TEST=runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
> TEST=runtime/vm/object_test.cc
>
> Change-Id: I03e6b4a46212316254bf46ba3f2df333abaa686c
> Cq-Include-Trybots: luci.dart.try:vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-ffi-android-debug-arm64c-try,dart-sdk-mac-arm64-try,vm-kernel-mac-release-arm64-try,pkg-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-nnbd-win-release-ia32-try,vm-ffi-android-debug-arm-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-mac-debug-x64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,benchmark-linux-try,flutter-analyze-try,flutter-frontend-try,pkg-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229544
> Reviewed-by: Lasse Nielsen <lrn@google.com>
> Reviewed-by: Slava Egorov <vegorov@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Daco Harkes <dacoharkes@google.com>

TBR=lrn@google.com,vegorov@google.com,kustermann@google.com,rmacnak@google.com,dacoharkes@google.com

Change-Id: I991f6e49896d18a8d70210cf315d858b462d66c9
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: https://github.com/dart-lang/sdk/issues/47777
Cq-Include-Trybots: luci.dart.try:vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-ffi-android-debug-arm64c-try,dart-sdk-mac-arm64-try,vm-kernel-mac-release-arm64-try,pkg-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-nnbd-win-release-ia32-try,vm-ffi-android-debug-arm-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-mac-debug-x64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,benchmark-linux-try,flutter-analyze-try,flutter-frontend-try,pkg-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238080
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
2022-03-22 11:55:38 +00:00

281 lines
8.8 KiB
C++

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_MESSAGE_HANDLER_H_
#define RUNTIME_VM_MESSAGE_HANDLER_H_
#include <memory>
#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/message.h"
#include "vm/os_thread.h"
#include "vm/port_set.h"
#include "vm/thread_pool.h"
namespace dart {
// A MessageHandler is an entity capable of accepting messages.
class MessageHandler {
protected:
MessageHandler();
public:
enum MessageStatus {
kOK, // We successfully handled a message.
kError, // We encountered an error handling a message.
kRestart, // The VM is restarting.
kShutdown, // The VM is shutting down.
};
static const char* MessageStatusString(MessageStatus status);
virtual ~MessageHandler();
// Allow subclasses to provide a handler name.
virtual const char* name() const;
typedef uword CallbackData;
typedef MessageStatus (*StartCallback)(CallbackData data);
typedef void (*EndCallback)(CallbackData data);
// Runs this message handler on the thread pool.
//
// Before processing messages, the optional StartFunction is run.
//
// A message handler will run until it terminates either normally or
// abnormally. Normal termination occurs when the message handler
// no longer has any live ports. Abnormal termination occurs when
// HandleMessage() indicates that an error has occurred during
// message processing.
// Returns false if the handler terminated abnormally, otherwise it
// returns true.
bool Run(ThreadPool* pool,
StartCallback start_callback,
EndCallback end_callback,
CallbackData data);
// Handles the next message for this message handler. Should only
// be used when not running the handler on the thread pool (via Run
// or RunBlocking).
//
// Returns true on success.
MessageStatus HandleNextMessage();
// Handles any OOB messages for this message handler. Can be used
// even if the message handler is running on the thread pool.
//
// Returns true on success.
MessageStatus HandleOOBMessages();
// Blocks the thread on a condition variable until a message arrives, and then
// handles all messages.
MessageStatus PauseAndHandleAllMessages(int64_t timeout_millis);
// Returns true if there are pending OOB messages for this message
// handler.
bool HasOOBMessages();
// Returns true if there are pending normal messages for this message
// handler.
bool HasMessages();
// A message handler tracks how many live ports it has.
bool HasLivePorts() const { return live_ports_ > 0; }
intptr_t live_ports() const { return live_ports_; }
bool paused() const { return paused_ > 0; }
void increment_paused() { paused_++; }
void decrement_paused() {
ASSERT(paused_ > 0);
paused_--;
}
#if !defined(PRODUCT)
void DebugDump();
bool should_pause_on_start() const { return should_pause_on_start_; }
void set_should_pause_on_start(bool should_pause_on_start) {
should_pause_on_start_ = should_pause_on_start;
}
bool is_paused_on_start() const { return is_paused_on_start_; }
bool should_pause_on_exit() const { return should_pause_on_exit_; }
void set_should_pause_on_exit(bool should_pause_on_exit) {
should_pause_on_exit_ = should_pause_on_exit;
}
bool is_paused_on_exit() const { return is_paused_on_exit_; }
// Timestamp of the paused on start or paused on exit.
int64_t paused_timestamp() const { return paused_timestamp_; }
bool ShouldPauseOnStart(MessageStatus status) const;
bool ShouldPauseOnExit(MessageStatus status) const;
void PausedOnStart(bool paused);
void PausedOnExit(bool paused);
#endif
// Gives temporary ownership of |queue| and |oob_queue|. Using this object
// has the side effect that no OOB messages will be handled if a stack
// overflow interrupt is delivered.
class AcquiredQueues : public ValueObject {
public:
explicit AcquiredQueues(MessageHandler* handler);
~AcquiredQueues();
MessageQueue* queue() {
if (handler_ == NULL) {
return NULL;
}
return handler_->queue_;
}
MessageQueue* oob_queue() {
if (handler_ == NULL) {
return NULL;
}
return handler_->oob_queue_;
}
private:
MessageHandler* handler_;
SafepointMonitorLocker ml_;
friend class MessageHandler;
};
#if defined(DEBUG)
// Check that it is safe to access this message handler.
//
// For example, if this MessageHandler is an isolate, then it is
// only safe to access it when the MessageHandler is the current
// isolate.
virtual void CheckAccess();
#endif
protected:
// ------------ START PortMap API ------------
// These functions should only be called from the PortMap.
// Does this message handler correspond to the current isolate?
virtual bool IsCurrentIsolate() const { return false; }
// Return Isolate to which this message handler corresponds to.
virtual Isolate* isolate() const { return NULL; }
// Posts a message on this handler's message queue.
// If before_events is true, then the message is enqueued before any pending
// events, but after any pending isolate library events.
void PostMessage(std::unique_ptr<Message> message,
bool before_events = false);
// Notifies this handler that a port is being closed.
void ClosePort(Dart_Port port);
// Notifies this handler that all ports are being closed.
void CloseAllPorts();
// Returns true if the handler is owned by the PortMap.
//
// This is used to delete handlers when their last live port is closed.
virtual bool OwnedByPortMap() const { return false; }
// Requests deletion of this message handler when the next task
// completes.
void RequestDeletion();
void increment_live_ports();
void decrement_live_ports();
// ------------ END PortMap API ------------
// Custom message notification. Optionally provided by subclass.
virtual void MessageNotify(Message::Priority priority);
// Handles a single message. Provided by subclass.
//
// Returns true on success.
virtual MessageStatus HandleMessage(std::unique_ptr<Message> message) = 0;
virtual void NotifyPauseOnStart() {}
virtual void NotifyPauseOnExit() {}
// TODO(iposva): Set a local field before entering MessageHandler methods.
Thread* thread() const { return Thread::Current(); }
private:
friend class PortMap;
friend class MessageHandlerTestPeer;
friend class MessageHandlerTask;
struct PortSetEntry : public PortSet<PortSetEntry>::Entry {};
// Called by MessageHandlerTask to process our task queue.
void TaskCallback();
// Checks if we have a slot for idle task execution, if we have a slot
// for idle task execution it is scheduled immediately or we wait for
// idle expiration and then attempt to schedule the idle task.
// Returns true if their is scope for idle task execution so that we
// can loop back to handle more messages or false if idle tasks are not
// scheduled.
bool CheckIfIdleLocked(MonitorLocker* ml);
// Triggers a run of the idle task.
void RunIdleTaskLocked(MonitorLocker* ml);
// NOTE: These two functions release and reacquire the monitor, you may
// need to call HandleMessages to ensure all pending messages are handled.
void PausedOnStartLocked(MonitorLocker* ml, bool paused);
void PausedOnExitLocked(MonitorLocker* ml, bool paused);
// Dequeue the next message. Prefer messages from the oob_queue_ to
// messages from the queue_.
std::unique_ptr<Message> DequeueMessage(Message::Priority min_priority);
void ClearOOBQueue();
// Handles any pending messages.
MessageStatus HandleMessages(MonitorLocker* ml,
bool allow_normal_messages,
bool allow_multiple_normal_messages);
Monitor monitor_; // Protects all fields in MessageHandler.
MessageQueue* queue_;
MessageQueue* oob_queue_;
// This flag is not thread safe and can only reliably be accessed on a single
// thread.
bool oob_message_handling_allowed_;
bool paused_for_messages_;
PortSet<PortSetEntry>
ports_; // Only accessed by [PortMap], protected by [PortMap]s lock.
intptr_t live_ports_; // The number of open ports, including control ports.
intptr_t paused_; // The number of pause messages received.
#if !defined(PRODUCT)
bool should_pause_on_start_;
bool should_pause_on_exit_;
bool is_paused_on_start_;
bool is_paused_on_exit_;
int64_t paused_timestamp_;
#endif
bool task_running_;
bool delete_me_;
ThreadPool* pool_;
StartCallback start_callback_;
EndCallback end_callback_;
CallbackData callback_data_;
DISALLOW_COPY_AND_ASSIGN(MessageHandler);
};
} // namespace dart
#endif // RUNTIME_VM_MESSAGE_HANDLER_H_