Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native messages marshalling and main loop #421

Closed
Kofhein opened this issue Sep 5, 2024 · 2 comments · Fixed by #422
Closed

Native messages marshalling and main loop #421

Kofhein opened this issue Sep 5, 2024 · 2 comments · Fixed by #422
Labels
bug Something isn't working

Comments

@Kofhein
Copy link
Contributor

Kofhein commented Sep 5, 2024

Currently there are several issues with main loop

  1. When it comes to 60 fps 4K video playback - wait duration between next dispatching is too big ( I had to change sleep time from milliseconds to microseconds, but I'm using pretty powerful device based on Intel 12th gen, so I had insignificant CPU usage increase )
  2. All the plugins I've developed so far were crashing in debug mode because message wasn't sent on main (platform thread) and it cause exception like this to be thrown
    [FATAL:flutter/fml/memory/weak_ptr.h(109)] Check failed: (checker_.checker).IsCreationThreadCurrent().
    In order to resolve this issue I've added MessageDispatcher class
#ifndef __FLUTTER_ELINUX_MESSAGE_DISPATCHER__
#define __FLUTTER_ELINUX_MESSAGE_DISPATCHER__

#include <queue>
#include <mutex>
#include <functional>

class MessageDispatcher {
public:
    static MessageDispatcher& Instance() {
        static MessageDispatcher instance;
        return instance;
    }

    MessageDispatcher(MessageDispatcher const&) = delete;
    void operator=(MessageDispatcher const&) = delete;

    void PostMessageToMainThread(std::function<void()> message) {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        task_queue_.push(std::move(message));
    }

    void ProcessQueue() {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        while (!task_queue_.empty()) {
            auto task = std::move(task_queue_.front());
            task_queue_.pop();
            task();
        }
    }

private:
    MessageDispatcher() {}

    std::queue<std::function<void()>> task_queue_;
    std::mutex queue_mutex_;
};

#endif

Update Binary messanger interface to be able to provide access to it
binary_messanger.h

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_

#include <functional>
#include <string>

#include "message_dispatcher.h"

namespace flutter {

// A binary message reply callback.
//
// Used for submitting a binary reply back to a Flutter message sender.
typedef std::function<void(const uint8_t* reply, size_t reply_size)>
    BinaryReply;

// A message handler callback.
//
// Used for receiving messages from Flutter and providing an asynchronous reply.
typedef std::function<
    void(const uint8_t* message, size_t message_size, BinaryReply reply)>
    BinaryMessageHandler;

// A protocol for a class that handles communication of binary data on named
// channels to and from the Flutter engine.
class BinaryMessenger {
 public:
  virtual ~BinaryMessenger() = default;

  // Sends a binary message to the Flutter engine on the specified channel.
  //
  // If |reply| is provided, it will be called back with the response from the
  // engine.
  virtual void Send(const std::string& channel,
                    const uint8_t* message,
                    size_t message_size,
                    BinaryReply reply = nullptr) const = 0;

  // Registers a message handler for incoming binary messages from the Flutter
  // side on the specified channel.
  //
  // Replaces any existing handler. Provide a null handler to unregister the
  // existing handler.
  virtual void SetMessageHandler(const std::string& channel,
                                 BinaryMessageHandler handler) = 0;

  virtual MessageDispatcher & GetMessageDispatcher() const = 0;
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_

binary_messenger_impl.h

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_BINARY_MESSENGER_IMPL_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_BINARY_MESSENGER_IMPL_H_

#include <flutter_messenger.h>

#include <map>
#include <string>

#include "include/flutter/binary_messenger.h"

namespace flutter {

// Wrapper around a FlutterDesktopMessengerRef that implements the
// BinaryMessenger API.
class BinaryMessengerImpl : public BinaryMessenger {
 public:
  explicit BinaryMessengerImpl(FlutterDesktopMessengerRef core_messenger);

  virtual ~BinaryMessengerImpl();

  // Prevent copying.
  BinaryMessengerImpl(BinaryMessengerImpl const&) = delete;
  BinaryMessengerImpl& operator=(BinaryMessengerImpl const&) = delete;

  // |flutter::BinaryMessenger|
  void Send(const std::string& channel,
            const uint8_t* message,
            size_t message_size,
            BinaryReply reply) const override;

  // |flutter::BinaryMessenger|
  void SetMessageHandler(const std::string& channel,
                         BinaryMessageHandler handler) override;

  MessageDispatcher & GetMessageDispatcher() const override;

 private:
  // Handle for interacting with the C API.
  FlutterDesktopMessengerRef messenger_;

  // A map from channel names to the BinaryMessageHandler that should be called
  // for incoming messages on that channel.
  std::map<std::string, BinaryMessageHandler> handlers_;
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_BINARY_MESSENGER_IMPL_H_

in core_implementations.cc (or in header itself ) provide access to this singleton
MessageDispatcher & BinaryMessengerImpl::GetMessageDispatcher() const { return MessageDispatcher::Instance();};

Then add call for processing marshalled messages inside flutter_window.Run() main loop
flutter_view_controller_->engine()->messenger()->GetMessageDispatcher().ProcessQueue();

And from plugins just send eventSink messages using this dispacher

    auto track_handler = std::make_unique<VideoPlayerTrackStreamHandlerImpl>(
        // OnPositionUpdated
        [texture_id, host = this]() {
          host->plugin_registrar_->messenger()->GetMessageDispatcher().PostMessageToMainThread(
            [=](){

            host->SendPositionEventMessage(texture_id);
            }
          );
        },
        [texture_id, host = this]() {
          host->plugin_registrar_->messenger()->GetMessageDispatcher().PostMessageToMainThread(
            [=](){

          host->SendTrackCompletedEventMessage(texture_id);
            });
        });

This is a quick solution, maybe there are better wayt o do this, but there's not that much places where it can be accessed from both plugin and main loop.
Basically I wanted to hide everything not to involve plugin by wrapping calls inside Send method of BinaryMessangerImpl, but there was some issue when I wrapped - error about string not UTF-8 appeared and messages were not properly handled for some reason.

@HidenoriMatsubayashi HidenoriMatsubayashi added the bug Something isn't working label Sep 5, 2024
@HidenoriMatsubayashi
Copy link
Contributor

@Kofhein Thank you for reporting this. If possible, could you please create a PR to fix this issue? I think your solution sounds reasonable.

@Kofhein
Copy link
Contributor Author

Kofhein commented Sep 6, 2024

@HidenoriMatsubayashi, I created this #422 this looks better and it doesn't require any changes in plugins, should work after updating embedder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants