Skip to content

tvOS: Enable Platform Service extension#9313

Open
jkim-julie wants to merge 1 commit intoyoutube:mainfrom
jkim-julie:platform-service
Open

tvOS: Enable Platform Service extension#9313
jkim-julie wants to merge 1 commit intoyoutube:mainfrom
jkim-julie:platform-service

Conversation

@jkim-julie
Copy link
Collaborator

This enables PlatformService cobalt extension and adds ‘dev.cobalt.coat.clientloginfo’ which has the same behaviour with C25.

Bug: 487879114

@jkim-julie jkim-julie requested a review from a team as a code owner March 2, 2026 02:07
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

🤖 Gemini Suggested Commit Message


tvos: Enable Platform Service extension

Enable the PlatformService Cobalt extension for tvOS. This change
introduces the 'dev.cobalt.coat.clientloginfo' service, which provides
client-side logging information such as the display refresh rate and
maximum frames per second. This mirrors the functionality available in
Cobalt C25 releases, allowing applications to retrieve critical
performance metrics for monitoring and debugging on tvOS devices.

Bug: 487879114

💡 Pro Tips for a Better Commit Message:

  1. Influence the Result: Want to change the output? You can write custom prompts or instructions directly in the Pull Request description. The model uses that text to generate the message.
  2. Re-run the Generator: Post a comment with: /generate-commit-message

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enables the Platform Service extension for tvOS, specifically for the dev.cobalt.coat.clientloginfo service, and implements logic to calculate and report the display refresh rate using CADisplayLink. A critical thread-safety issue was identified, as accessing UIKit properties from a background thread could lead to application crashes, potentially exploitable for a Denial of Service (DoS) attack. Additionally, the review highlighted a critical memory leak due to a retain cycle with CADisplayLink, a potential correctness issue with run loop selection, and areas for improvement in parameter handling and consistency within the new platform service implementation.

@jkim-julie jkim-julie force-pushed the platform-service branch 2 times, most recently from 343dd99 to ad79394 Compare March 2, 2026 04:20
@jkim-julie
Copy link
Collaborator Author

cc: @Gyuyoung @abhijeetk @rakuco

This enables PlatformService cobalt extension and adds
‘dev.cobalt.coat.clientloginfo’ which has the same behaviour
with C25.

Bug: 487879114
Comment on lines +58 to +69
const char* kClientLogInfoServiceName = "dev.cobalt.coat.clientloginfo";

bool Has(const char* name) {
if (!name) {
return false;
}

if (strcmp(name, kClientLogInfoServiceName) == 0) {
return true;
}

return false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's take the opportunity and use more C++ here:

constexpr std::string_view kClientLogInfoServiceName = "...";

bool Has(const char* name) {
  if (!name) {
    return false;
  }
  return name == kClientLogInfoServiceName;
}

#include "starboard/extension/platform_service.h"
#import "starboard/tvos/shared/starboard_application.h"

typedef struct CobaltExtensionPlatformServicePrivate {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to exist at all, context and receive_callback are not used anywhere, so Send()'s implementation could be merged directly into Send() below.

uint64_t* output_length,
bool* invalid_state) {
if (!output_length || !invalid_state) {
return NULL;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/NULL/nullptr/

SBDGetApplication().displayRefreshRate);

*output_length = string.size() + 1;
char* output = (char*)malloc(*output_length);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
char* output = (char*)malloc(*output_length);
auto* output = malloc(*output_length);

char* output = (char*)malloc(*output_length);
if (!output) {
*invalid_state = true;
return NULL;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return NULL;
return nullptr;

SBDGetApplication().maximumFramesPerSecond,
SBDGetApplication().displayRefreshRate);

*output_length = string.size() + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*output_length = string.size() + 1;
// Other extensions do not include a NUL-terminating byte. This is done here for compatibility with C25.
*output_length = string.size() + 1;

}

*invalid_state = false;
std::string string = starboard::FormatString(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::string string = starboard::FormatString(
const std::string string = starboard::FormatString(

}
}

- (void)update:(CADisplayLink*)sender {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried it out myself, but I think it should be possible to plug into https://source.chromium.org/chromium/chromium/src/+/main:components/viz/common/frame_sinks/external_begin_frame_source_ios.mm which already sets up a CADisplayLink, performs the interval calculation and dispatches it.

If you subclass SimpleBeginFrameObserver, you should be able to retrieve interval as frame_interval. If this works, you can just send this result to SBDGetApplication() and perform the division when the value is requested, as I don't think Kabuki will need this on every frame update.

NSInteger _maximumFramesPerSecond;

// The last display refresh rate.
std::atomic<double> _lastDisplayRefreshRate;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be an atomic variable, especially if you use PostTask() from cobalt shell to make the writes sequential.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants