Skip to content

Adding a Test to check if Swift refcounting is really working. #35368

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

nmahendru
Copy link
Contributor

@nmahendru nmahendru commented Oct 17, 2024

39d68c46d92e77d3a7a19c86d3fd8bc15c666c4f

Adding a Test to check if Swift refcounting is really working.
https://bugs.webkit.org/show_bug.cgi?id=281608
rdar://problem/138056283

Reviewed by NOBODY (OOPS!).

Check that reference count is getting incremented in swift.

* Source/WebGPU/WebGPU.xcodeproj/project.pbxproj:
* Source/WebGPU/WebGPU/TestPlumbing.swift: Added.
(getRefCount(_:)):
* Source/WebGPU/WebGPU/WebGPU.h:
(retainCpp):
(releaseCpp):
* Source/WebGPU/WebGPU/WebGPUExt.h:
* Source/WebGPU/WebGPU/WebGPUSwift.mm: Added.
(WebGPUTest::CppRefCounted::getSwiftRefCount):
* Tools/TestWebKitAPI/Configurations/TestWebKitAPIBase.xcconfig:
* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WebGPUSwift.mm: Added.
(TEST(WebGPUSwiftTest, CppReferenceCounting)):

1b70bf0

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
❌ 🧪 style ❌ 🛠 ios ❌ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win
✅ 🧪 bindings ❌ 🛠 ios-sim ❌ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ❌ 🧪 win-tests
✅ 🧪 webkitperl ❌ 🧪 ios-wk2 ❌ 🧪 api-mac ✅ 🧪 api-wpe
✅ 🧪 webkitpy ❌ 🧪 ios-wk2-wpt ❌ 🧪 mac-wk1 ✅ 🛠 wpe-cairo
✅ 🛠 🧪 jsc ❌ 🧪 api-ios ❌ 🧪 mac-wk2 ✅ 🛠 gtk
✅ 🛠 🧪 jsc-arm64 ❌ 🛠 vision ❌ 🧪 mac-AS-debug-wk2 ⏳ 🧪 gtk-wk2
❌ 🛠 vision-sim ❌ 🧪 mac-wk2-stress ✅ 🧪 api-gtk
⏳ 🧪 vision-wk2 ❌ 🧪 mac-intel-wk2 ✅ 🛠 jsc-armv7
❌ 🛠 tv ✅ 🧪 jsc-armv7-tests
❌ 🛠 tv-sim
❌ 🛠 watch
❌ 🛠 watch-sim

@nmahendru nmahendru self-assigned this Oct 17, 2024
@nmahendru nmahendru added the WebGPU For bugs in WebGPU label Oct 17, 2024
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 17, 2024
@nmahendru nmahendru removed the merging-blocked Applied to prevent a change from being merged label Oct 17, 2024
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 17, 2024
@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from d0eac26 to e0d3936 Compare October 17, 2024 18:52
@nmahendru nmahendru removed the merging-blocked Applied to prevent a change from being merged label Oct 17, 2024
@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from e0d3936 to 39d68c4 Compare October 17, 2024 19:00
@nmahendru nmahendru marked this pull request as ready for review October 17, 2024 21:01
}

public static func getRefCountOptimizedOut(obj: WebGPUTest.CppRefCounted) -> UInt32 {
return obj.refCountProxy()
Copy link
Contributor

Choose a reason for hiding this comment

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

the return here can be omitted with recent Swift versions

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

the year should be updated

*/

public class TestPlumbing {
public static func getRefCount(obj: WebGPUTest.CppRefCounted) -> UInt32 {
Copy link
Contributor

Choose a reason for hiding this comment

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

I've seen this in some of your other PRs, but never asked: Is it intentional that you're using the fully qualified <module>.<type> type names in function declarations? Normally I only see fully-qualified types used to disambiguate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have never really found a way around it which is not:
typelias CppRefCounted = WebGPUTest.CppRefCounted.
I'll do that for now and ask around as to how C++ namespaces(WebGPUTest) are actually getting imported.

Copy link
Contributor

Choose a reason for hiding this comment

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

If this is how the interop layer imports them, that's fine with me. I think the typealias would be unnecessary. It's just a little weird since typically in Swift, once you import the module (import WebGPUTest), all these types would be able to be named without being fully-qualified.

#if ENABLE(WEBGPU_SWIFT)
TEST(WebGPUSwiftTest, CppReferenceCounting)
{
auto obj = WebGPUTest::CppRefCounted::create();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto obj = WebGPUTest::CppRefCounted::create();
Ref obj = WebGPUTest::CppRefCounted::create();

(per smart ptr guidelines)


FRAMEWORK_SEARCH_PATHS = $(FRAMEWORK_SEARCH_PATHS_$(WK_COCOA_TOUCH));
FRAMEWORK_SEARCH_PATHS_ = $(inherited) $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/WebKit.framework/Versions/A/Frameworks;
FRAMEWORK_SEARCH_PATHS_cocoatouch = $(inherited);

PROJECT_HEADER_SEARCH_PATHS = $(SRCROOT)/../../Source/WebKit/Platform $(SRCROOT)/../../Source/WebKit/Platform/IPC $(SRCROOT)/../../Source/WebKit/Platform/IPC/darwin $(SRCROOT)/../../Source/WebKit/Platform/IPC/cocoa $(SRCROOT)/../../Source/WebKit/Shared $(SRCROOT)/../../Source/WebKit/Shared/Cocoa $(SRCROOT)/../../Source/WebKit/Shared/cf $(SRCROOT)/../../Source/WebKit/Shared/API/C $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit $(inherited);

HEADER_SEARCH_PATHS = $(inherited) $(OBJROOT)/WebGPU.build/WebGPU.build/DerivedSources $(OBJROOT)/WebGPU.build/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/WebGPU.build/DerivedSource
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you remind me if we have precedent for reaching into other projects' intermediate build dir like this? It's pretty gross, and I worry that you are making assumptions about build directory structure that won't always be true (e.g. for a person who uses the Xcode-default derived data dir).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a precedent in PAL<=>WebCore. and yes, It is gross.
Although, I think for this PR this is not even needed anymore. Sorry for that. It's just a remnant of how I was thinking of doing this earlier which I should have removed.

@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from 39d68c4 to d4b414f Compare October 17, 2024 22:42
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 18, 2024
Comment on lines 32 to 40
unsigned WebGPUTest::CppRefCounted::getSwiftRefCount()
{
return WebGPU::TestPlumbing::getRefCount(this);
}

unsigned WebGPUTest::CppRefCounted::getSwiftRefCountOptimizedOut()
{
return WebGPU::TestPlumbing::getRefCountOptimizedOut(this);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this set of wrappers required? I don't think it is. Empty abstraction makes code hard to read and edit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to expose the swift functions out of WebGPU.framework as a C++ API so that I can call it in TestWebKitAPI. Do you know of another way to do it ? If I directly want to call TestPlumbing then I will have to enable Interop in the test binary also.. which I really did not want to do.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we want to avoid enabling interop in our unit tests? Don't we want to unit test interop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we want to unit test interop. And that's why this PR.
I just did not want to turn on interop in TestWebKitAPI yet unless we really have a pressing need for it. Usually, enabling interop involves yet another series of #ifndef __swift__ which just adds debt to cleanup later.
If you strongly feel that we should just enable interop in TestWebKitAPI now, then I can work on that.


inline void retainCpp(WebGPUTest::CppRefCounted* obj)
{
WTF::retainRefCounted(obj);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should rename WTF::retainRefCounted to ref(). Having two names for one thing is confusing. Also, you can remove the RELEASE_ASSERT, static_assert, and static_cast. Just have it call object->ref().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are three of these:

  1. RefCounted
  2. ThreadSafeRefCounted
  3. ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr

All three have Global wrappers(which Swift mandates). So I cannot rename all of them as ref ?

Also, curious if you are asking me to remove the asserts for perfomance ? I have just added them to make sure I am calling the right ref

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm saying something way more basic: If obj is a refcounted WebKit object, it has a member function named 'ref'.

I want to remove the cast because it's not type-safe, and not needed. I want to remove the asserts because they're just there to guard the cast.


}

inline void retainCpp(WebGPUTest::CppRefCounted* obj)
Copy link
Contributor

Choose a reason for hiding this comment

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

I would call this function 'ref', since it's a free function whose job is to call the 'ref' member function.

But also: If you make a free function in WTF, template<typename T> void ref(T* object) { object->ref(); } you may not need a separate function here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried that when I was adding it to the RefCounted classes but templates don't work with swift.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right! So we do need separate function overloads per class, but we can still make them free functions named ref: void ref(WebGPUTest::CppRefCounted* object) { object->ref(); }


inline void releaseCpp(WebGPUTest::CppRefCounted* obj)
{
WTF::releaseRefCounted(obj);
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Comment on lines 1887 to 1889
unsigned refCountProxy() { return refCount(); }
WGPU_EXPORT unsigned getSwiftRefCount();
WGPU_EXPORT unsigned getSwiftRefCountOptimizedOut();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need any of this abstraction? Can't the caller just call refCount()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

to expose the API in Swift in C++ and use it in TestWebKitAPI without enabling interop there.

#include <wtf/RetainReleaseSwift.h>
#if ENABLE(WEBGPU_SWIFT)
namespace WebGPUTest {
class CppRefCounted: public RefCounted<CppRefCounted> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this class move to TestWebKitAPI?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only if I enable interop there which I would rather not do and avoid the complexity.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we want to write unit tests for Swift, we're going to need to make Swift unit testing possible.

Would it help to make Swift unit testing a separate build target? TestWebKitAPI supports that, and already has TestIPC, TestWGSL, TestWTF, etc.

Comment on lines 1886 to 1887
// Swift is not importing const methods and RefCount::refCount() is const
unsigned refCountProxy() { return refCount(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

You can make an #if __swift__ non-const overload for refCount() in the RefCounted class, with a FIXME and a Radar for not importing const methods.

Comment on lines 28 to 38
public class TestPlumbing {
public static func getRefCount(obj: CppRefCounted) -> UInt32 {
// Just an API call to ensure swift does not optimize this out.
let refCount = obj.refCountProxy()
let _ = "The ref count is \(refCount)"
return refCount
}

public static func getRefCountOptimizedOut(obj: CppRefCounted) -> UInt32 {
obj.refCountProxy()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems kinda brittle. I don't think we need it?

The goal is to determine that the object is allocated with the right refcount, right? If so, we want to export a Swift function that will allocate and return the object, and then we can test the refcount of the result directly in our caller TestWebKitAPI code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm.. okie.. I can remove that. I just wanted to highlight the optimization via a test but it's not important.

@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from d4b414f to fff4bc9 Compare October 19, 2024 01:00
@nmahendru nmahendru requested a review from JonWBedard as a code owner October 19, 2024 01:00
@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from fff4bc9 to ef20a4e Compare October 19, 2024 01:40
@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from ef20a4e to 3fbe91f Compare November 4, 2024 19:00
https://bugs.webkit.org/show_bug.cgi?id=281608
rdar://problem/138056283

Reviewed by NOBODY (OOPS!).

Check that reference count is getting incremented in swift.

* Source/WTF/wtf/RefCounted.h:
(WTF::RefCountedBase::ref):
(WTF::RefCountedBase::ref const): Deleted.
* Source/WebGPU/WebGPU/Buffer.mm:
(WebGPU::Buffer::copy):
* Source/WebGPU/WebGPU/WebGPUExt.h:
* Tools/Scripts/run-api-tests:
(parse_args):
* Tools/Scripts/webkitpy/port/darwin.py:
(DarwinPort):
* Tools/TestWebKitAPI/Configurations/TestWebGPU.xcconfig: Added.
* Tools/TestWebKitAPI/Configurations/TestWebKitAPIBase.xcconfig:
* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/WebGPU/RefCountTests.mm: Added.
(TEST(TestWebGPU, ReferenceCounting)):
* Tools/TestWebKitAPI/Tests/WebGPU/TestWebGPU.h: Added.
(refCpp):
(derefCpp):
* Tools/TestWebKitAPI/Tests/WebGPU/TestWebGPU.mm: Added.
(Cpp::CppRefCounted::getSwiftRefCount):
* Tools/TestWebKitAPI/Tests/WebGPU/TestWebGPU.modulemap: Added.
* Tools/TestWebKitAPI/Tests/WebGPU/Tests.swift: Added.
(getRefCount(_:)):
@nmahendru nmahendru force-pushed the eng/WebGPU-Swift-Reference-Counting-Tests branch from 3fbe91f to 1b70bf0 Compare November 4, 2024 19:19
@mwyrzykowski mwyrzykowski marked this pull request as draft June 30, 2025 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merging-blocked Applied to prevent a change from being merged WebGPU For bugs in WebGPU
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants