e2efffa005
This reland commit5a32d8bc7cwith a fix for thread leak (Issue #56717): when `ThreadPool` is shutting down asynchronously the last worker should detach itself to prevent leaking associated low-level data structures, because no thread will join it. A hang in service isolate shutdown (caused by an existing bug) was fixed by commit157a0dc7f9. This CL turns native ports into a thin abstraction over underlying thread pool instead of building them as full fledged MessageHandler. This allows to easily implement a variation of native ports which can handle messages concurrently with the given degree of concurrency. This type of port can be used to greatly simplify implementation of IOService - which previously had to do its own concurrency management on top of "single threaded" native ports. This capability is exposed as `Dart_NewConcurrentNativePort` API. The new implementation is in general much cleaner then the old one with one exception: `Dart_CloseNativePort` API has unfortunate design where underlying message handler is destroyed asynchronously and `Dart_CloseNativePort` returns immediately without waiting for pending tasks to complete. Implementing this on top of `ThreadPool` requires some changes to thread pool implementation. Issue https://github.com/dart-lang/sdk/issues/55844 Closes https://github.com/dart-lang/sdk/issues/56717 TEST=ci Change-Id: Ic68bfb60757685afd75c80a70cdec66cc13c149b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385000 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
270 lines
8.3 KiB
C++
270 lines
8.3 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.h"
|
|
#include "vm/port_set.h"
|
|
#include "vm/thread_pool.h"
|
|
|
|
namespace dart {
|
|
|
|
// A MessageHandler is an entity capable of accepting messages.
|
|
class MessageHandler : public PortHandler {
|
|
protected:
|
|
MessageHandler();
|
|
|
|
public:
|
|
enum MessageStatus {
|
|
kOK, // We successfully handled a message.
|
|
kError, // We encountered an error handling a message.
|
|
kShutdown, // The VM is shutting down.
|
|
};
|
|
static const char* MessageStatusString(MessageStatus status);
|
|
|
|
virtual ~MessageHandler();
|
|
|
|
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();
|
|
|
|
#if defined(TESTING)
|
|
std::unique_ptr<Message> StealOOBMessage();
|
|
#endif
|
|
|
|
// Returns true if there are pending normal messages for this message
|
|
// handler.
|
|
bool HasMessages();
|
|
|
|
// Whether to keep this message handler alive or whether it should shutdown.
|
|
virtual bool KeepAliveLocked() { return true; }
|
|
|
|
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_ == nullptr) {
|
|
return nullptr;
|
|
}
|
|
return handler_->queue_;
|
|
}
|
|
|
|
MessageQueue* oob_queue() {
|
|
if (handler_ == nullptr) {
|
|
return nullptr;
|
|
}
|
|
return handler_->oob_queue_;
|
|
}
|
|
|
|
private:
|
|
MessageHandler* handler_;
|
|
SafepointMonitorLocker ml_;
|
|
|
|
friend class MessageHandler;
|
|
};
|
|
|
|
protected:
|
|
// 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(); }
|
|
|
|
// 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) override;
|
|
|
|
private:
|
|
template <typename GCVisitorType>
|
|
friend void MournFinalizerEntry(GCVisitorType*, FinalizerEntryPtr);
|
|
friend class PortMap;
|
|
friend class MessageHandlerTestPeer;
|
|
friend class MessageHandlerTask;
|
|
|
|
// ------------ START PortMap API ------------
|
|
// These functions should only be called from the PortMap.
|
|
// Implementaion of PortHandler API.
|
|
|
|
const char* name() const override;
|
|
|
|
void OnPortClosed(Dart_Port port) override;
|
|
|
|
void Shutdown() override {
|
|
// Nothing to do.
|
|
}
|
|
|
|
// Return Isolate to which this message handler corresponds to.
|
|
Isolate* isolate() const override { return nullptr; }
|
|
|
|
PortSet<PortSetEntry>* ports(PortMap::Locker& locker) override {
|
|
return &ports_;
|
|
}
|
|
|
|
// Notifies this handler that all ports are being closed.
|
|
void OnAllPortsClosed();
|
|
// ------------ END PortMap API ------------
|
|
|
|
// 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_;
|
|
|
|
// Only accessed by [PortMap], protected by [PortMap]s lock. See ports()
|
|
// getter.
|
|
PortSet<PortSetEntry> 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_;
|
|
// When isolate gets paused, remember the status of the message being
|
|
// processed so that we can resume correctly(into potentially not-OK status).
|
|
MessageStatus remembered_paused_on_exit_status_;
|
|
int64_t paused_timestamp_;
|
|
#endif
|
|
bool task_running_;
|
|
ThreadPool* pool_;
|
|
StartCallback start_callback_;
|
|
EndCallback end_callback_;
|
|
CallbackData callback_data_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(MessageHandler);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_MESSAGE_HANDLER_H_
|