Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions starboard/android/shared/video_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
#include "cobalt/android/jni_headers/VideoSurfaceView_jni.h"
#include "starboard/android/shared/starboard_bridge.h"
#include "starboard/common/log.h"
#include "starboard/common/no_destructor.h"
#include "starboard/common/once.h"
#include "starboard/configuration.h"
#include "starboard/shared/gles/gl_call.h"
#include "third_party/jni_zero/jni_zero.h"

namespace starboard {

Expand All @@ -38,8 +40,12 @@ namespace {

// Global video surface pointer mutex.
SB_ONCE_INITIALIZE_FUNCTION(std::mutex, GetViewSurfaceMutex)
// Global pointer to the single video surface.
jobject g_j_video_surface = NULL;

jni_zero::ScopedJavaGlobalRef<jobject>& GetGlobalVideoSurface() {
static NoDestructor<jni_zero::ScopedJavaGlobalRef<jobject>> instance;
return *instance;
}

// Global pointer to the single video window.
ANativeWindow* g_native_video_window = NULL;
// Global video surface pointer holder.
Expand All @@ -58,16 +64,15 @@ void JNI_VideoSurfaceView_OnVideoSurfaceChanged(
g_video_surface_holder->OnSurfaceDestroyed();
g_video_surface_holder = NULL;
}
if (g_j_video_surface) {
env->DeleteGlobalRef(g_j_video_surface);
g_j_video_surface = NULL;
if (GetGlobalVideoSurface()) {
GetGlobalVideoSurface().Reset();
}
Comment thread
kjyoun marked this conversation as resolved.
Outdated
if (g_native_video_window) {
ANativeWindow_release(g_native_video_window);
g_native_video_window = NULL;
}
if (surface) {
g_j_video_surface = env->NewGlobalRef(surface.obj());
GetGlobalVideoSurface().Reset(env, surface);
g_native_video_window = ANativeWindow_fromSurface(env, surface.obj());
}
Comment thread
kjyoun marked this conversation as resolved.
Outdated
}
Expand All @@ -82,19 +87,19 @@ bool VideoSurfaceHolder::IsVideoSurfaceAvailable() {
// surface and it is not held by any decoder, i.e.
// g_video_surface_holder is NULL.
std::lock_guard lock(*GetViewSurfaceMutex());
return !g_video_surface_holder && g_j_video_surface;
return !g_video_surface_holder && GetGlobalVideoSurface();
}

jobject VideoSurfaceHolder::AcquireVideoSurface() {
std::lock_guard lock(*GetViewSurfaceMutex());
if (g_video_surface_holder != NULL) {
return NULL;
}
if (!g_j_video_surface) {
if (!GetGlobalVideoSurface()) {
return NULL;
}
g_video_surface_holder = this;
return g_j_video_surface;
return GetGlobalVideoSurface().obj();
}

void VideoSurfaceHolder::ReleaseVideoSurface() {
Expand Down
2 changes: 2 additions & 0 deletions starboard/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ static_library("common") {
"metrics/stats_tracker.h",
"murmurhash2.cc",
"murmurhash2.h",
"no_destructor.h",
"pass_key.h",
"paths.cc",
"paths.h",
Expand Down Expand Up @@ -135,6 +136,7 @@ source_set("common_test") {
"log_unittest.cc",
"media_test.cc",
"memory_test.cc",
"no_destructor_test.cc",
"player_test.cc",
"queue_test.cc",
"rect_test.cc",
Expand Down
79 changes: 79 additions & 0 deletions starboard/common/no_destructor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2026 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef STARBOARD_COMMON_NO_DESTRUCTOR_H_
#define STARBOARD_COMMON_NO_DESTRUCTOR_H_

#include <new>
#include <type_traits>
#include <utility>

namespace starboard {

// Helper type to create a function-local static variable of type `T` when `T`
// has a non-trivial destructor. Storing a `T` in a `starboard::NoDestructor<T>`
// will prevent `~T()` from running, even when the variable goes out of scope.
//
// Useful when a variable has static storage duration but its type has a
// non-trivial destructor. Global constructors and destructors are discouraged:
// using a function-local static variable prevents the former, while using
// `starboard::NoDestructor<T>` prevents the latter.
//
// See base::NoDestructor for more details and caveats.
template <typename T>
class NoDestructor {
public:
static_assert(!(std::is_trivially_constructible_v<T> &&
std::is_trivially_destructible_v<T>),
"T is trivially constructible and destructible; please use a "
"constinit object of type T directly instead");

static_assert(
!std::is_trivially_destructible_v<T>,
"T is trivially destructible; please use a function-local static "
"of type T directly instead");

template <typename... Args>
explicit NoDestructor(Args&&... args) {
new (storage_) T(std::forward<Args>(args)...);
}

explicit NoDestructor(const T& x) { new (storage_) T(x); }
explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }

NoDestructor(const NoDestructor&) = delete;
NoDestructor& operator=(const NoDestructor&) = delete;

~NoDestructor() = default;

const T& operator*() const { return *get(); }
T& operator*() { return *get(); }

const T* operator->() const { return get(); }
T* operator->() { return get(); }

const T* get() const { return reinterpret_cast<const T*>(storage_); }
T* get() { return reinterpret_cast<T*>(storage_); }
Comment thread
kjyoun marked this conversation as resolved.

private:
alignas(T) char storage_[sizeof(T)];

#if defined(LEAK_SANITIZER)
T* storage_ptr_ = reinterpret_cast<T*>(storage_);
#endif // defined(LEAK_SANITIZER)
};

} // namespace starboard

#endif // STARBOARD_COMMON_NO_DESTRUCTOR_H_
97 changes: 97 additions & 0 deletions starboard/common/no_destructor_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2026 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "starboard/common/no_destructor.h"

#include <string>
#include <utility>
#include <vector>

#include "testing/gtest/include/gtest/gtest.h"

namespace starboard {
namespace {

struct NotreachedOnDestroy {
~NotreachedOnDestroy() {
ADD_FAILURE() << "~NotreachedOnDestroy should not be called";
}
};

TEST(NoDestructorTest, SkipsDestructors) {
NoDestructor<NotreachedOnDestroy> destructor_should_not_run;
}

struct UncopyableUnmovable {
UncopyableUnmovable() = default;
explicit UncopyableUnmovable(int value) : value(value) {}

UncopyableUnmovable(const UncopyableUnmovable&) = delete;
UncopyableUnmovable& operator=(const UncopyableUnmovable&) = delete;

int value = 1;
std::string dummy_to_make_nontrivial_destructor;
};

struct CopyOnly {
CopyOnly() = default;

CopyOnly(const CopyOnly&) = default;
CopyOnly& operator=(const CopyOnly&) = default;

CopyOnly(CopyOnly&&) = delete;
CopyOnly& operator=(CopyOnly&&) = delete;
};

struct MoveOnly {
MoveOnly() = default;

MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;

MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
};

struct ForwardingTestStruct {
ForwardingTestStruct(const CopyOnly&, MoveOnly&&) {}
std::string dummy_to_make_nontrivial_destructor;
};

TEST(NoDestructorTest, UncopyableUnmovable) {
static NoDestructor<UncopyableUnmovable> default_constructed;
EXPECT_EQ(1, default_constructed->value);

static NoDestructor<UncopyableUnmovable> constructed_with_arg(-1);
EXPECT_EQ(-1, constructed_with_arg->value);
}

TEST(NoDestructorTest, ForwardsArguments) {
CopyOnly copy_only;
MoveOnly move_only;

static NoDestructor<ForwardingTestStruct> test_forwarding(
copy_only, std::move(move_only));
}

TEST(NoDestructorTest, Accessors) {
static NoDestructor<std::string> awesome("awesome");

EXPECT_EQ("awesome", *awesome);
EXPECT_EQ(0, awesome->compare("awesome"));
EXPECT_EQ(0, awesome.get()->compare("awesome"));
}

} // namespace
} // namespace starboard
Loading