Unverified Commit 40bf4ebe authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

JS memory management. (#181)

parent 11b45fc5
...@@ -18,7 +18,6 @@ jobs: ...@@ -18,7 +18,6 @@ jobs:
macos: macos:
# The type of runner that the job will run on # The type of runner that the job will run on
runs-on: macos-10.15 runs-on: macos-10.15
# Steps represent a sequence of tasks that will be executed as part of the job # Steps represent a sequence of tasks that will be executed as part of the job
steps: steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
...@@ -35,7 +34,6 @@ jobs: ...@@ -35,7 +34,6 @@ jobs:
./gradlew -Pskiko.native.enabled=true publishToMavenLocal ./gradlew -Pskiko.native.enabled=true publishToMavenLocal
linux: linux:
runs-on: ubuntu-16.04 runs-on: ubuntu-16.04
# Steps represent a sequence of tasks that will be executed as part of the job # Steps represent a sequence of tasks that will be executed as part of the job
steps: steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
...@@ -61,6 +59,36 @@ jobs: ...@@ -61,6 +59,36 @@ jobs:
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev zip -y sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev zip -y
./gradlew jvmTest ./gradlew jvmTest
./gradlew publishToMavenLocal ./gradlew publishToMavenLocal
js:
runs-on: ubuntu-20.04
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
# Runs a set of commands using the runners shell
- shell: bash
run: |
cd ./skiko
sudo apt-get update -y
sudo apt-get install binutils build-essential -y
sudo apt-get install software-properties-common -y
sudo apt-get install python git curl wget -y
if [ -d ./emsdk ]; then
cd ./emsdk
git pull
else
git clone https://github.com/emscripten-core/emsdk.git
cd ./emsdk
fi
./emsdk install 2.0.29
./emsdk activate 2.0.29
source ./emsdk_env.sh
cd ..
./gradlew skikoJsJar
windows: windows:
runs-on: windows-2019 runs-on: windows-2019
steps: steps:
......
...@@ -117,7 +117,7 @@ kotlin { ...@@ -117,7 +117,7 @@ kotlin {
testTask { testTask {
testLogging.showStandardStreams = true testLogging.showStandardStreams = true
dependsOn(project.tasks.named("wasmCompile")) dependsOn(project.tasks.named("wasmCompile"))
useKarma() { useKarma {
useChromeHeadless() useChromeHeadless()
} }
} }
...@@ -338,15 +338,14 @@ project.tasks.register<Exec>("objcCompile") { ...@@ -338,15 +338,14 @@ project.tasks.register<Exec>("objcCompile") {
project.tasks.register<Exec>("wasmCompile") { project.tasks.register<Exec>("wasmCompile") {
dependsOn(skiaWasmDir) dependsOn(skiaWasmDir)
val skiaDir = skiaWasmDir.get().absolutePath
val inputDir = "$projectDir/src/jsMain/cpp" val inputDir = "$projectDir/src/jsMain/cpp"
val outDir = "$buildDir/wasm" val outDir = "$buildDir/wasm"
val names = File(inputDir).listFiles()!!.filter { it.name.endsWith(".cc") }.map { it.name.removeSuffix(".cc") } val names = File(inputDir).listFiles()!!.filter { it.name.endsWith(".cc") }.map { it.name.removeSuffix(".cc") }
val srcs = names.map { "$inputDir/$it.cc" }.toTypedArray() val srcs = names.map { "$inputDir/$it.cc" }.toTypedArray()
val outJs = "$outDir/skiko.js" val outJs = "$outDir/skiko.js"
val outWasm = "$outDir/skiko.wasm" val outWasm = "$outDir/skiko.wasm"
val skiaDir = skiaWasmDir.get().absolutePath
workingDir = File(outDir) workingDir = File(outDir)
val libs = fileTree("$skiaDir/out/Release-wasm-wasm").filter { it.name.endsWith(".a") }
commandLine = listOf( commandLine = listOf(
"emcc", "emcc",
*Arch.Wasm.clangFlags, *Arch.Wasm.clangFlags,
...@@ -355,12 +354,14 @@ project.tasks.register<Exec>("wasmCompile") { ...@@ -355,12 +354,14 @@ project.tasks.register<Exec>("wasmCompile") {
"-I$skiaDir/include/core", "-I$skiaDir/include/core",
"-I$skiaDir/include/effects", "-I$skiaDir/include/effects",
"-I$skiaDir/include/gpu", "-I$skiaDir/include/gpu",
"-std=c++17",
"--bind",
"-o", outJs, "-o", outJs,
*libs.files.map { it.absolutePath }.toTypedArray(),
*srcs *srcs
) )
argumentProviders.add(CommandLineArgumentProvider {
val skiaBinDir = "$skiaDir/out/${buildType.id}-wasm-wasm"
// We must compute this list after Skia unpacking task has finished.
listOf(skiaBinDir).findAllFiles(".a")
})
file(outDir).mkdirs() file(outDir).mkdirs()
inputs.files(srcs) inputs.files(srcs)
outputs.files(outJs, outWasm) outputs.files(outJs, outWasm)
...@@ -662,11 +663,6 @@ project.tasks.register<Jar>("skikoJsJar") { ...@@ -662,11 +663,6 @@ project.tasks.register<Jar>("skikoJsJar") {
} }
} }
project.tasks.register<JavaExec>("run") {
main = "org.jetbrains.skiko.MainKt"
classpath = files(skikoJvmRuntimeJar.map { it.archiveFile })
}
// disable unexpected native publications (default C++ publications are failing) // disable unexpected native publications (default C++ publications are failing)
tasks.withType<AbstractPublishToMaven>().configureEach { tasks.withType<AbstractPublishToMaven>().configureEach {
doFirst { doFirst {
...@@ -808,8 +804,7 @@ publishing { ...@@ -808,8 +804,7 @@ publishing {
afterEvaluate { afterEvaluate {
artifact(project.tasks.withType(KotlinNativeCompile::class.java) artifact(project.tasks.withType(KotlinNativeCompile::class.java)
.single { it.name.startsWith("compileKotlin") } // Exclude compileTestKotlin. .single { it.name.startsWith("compileKotlin") } // Exclude compileTestKotlin.
.outputs.getFiles().single { it.name.endsWith(".klib") } .outputs.files.single { it.name.endsWith(".klib") })
)
} }
} }
} }
......
...@@ -15,7 +15,7 @@ enum class OS(val id: String, val clangFlags: Array<String>) { ...@@ -15,7 +15,7 @@ enum class OS(val id: String, val clangFlags: Array<String>) {
enum class Arch(val id: String, val clangFlags: Array<String>) { enum class Arch(val id: String, val clangFlags: Array<String>) {
X64("x64", arrayOf("-arch", "x86_64")), X64("x64", arrayOf("-arch", "x86_64")),
Arm64("arm64", arrayOf("-arch", "arm64")), Arm64("arm64", arrayOf("-arch", "arm64")),
Wasm("wasm", emptyArray()) Wasm("wasm", arrayOf("-std=c++17", "--bind"))
} }
enum class SkiaBuildType( enum class SkiaBuildType(
......
...@@ -9,7 +9,7 @@ static void deleteBackendRenderTarget(GrBackendRenderTarget* rt) { ...@@ -9,7 +9,7 @@ static void deleteBackendRenderTarget(GrBackendRenderTarget* rt) {
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
extern "C" KPointer BackendRenderTarget_nGetFinalizer() { extern "C" KPointer BackendRenderTarget_nGetFinalizer() {
return static_cast<KPointer>(reinterpret_cast<uintptr_t>(&deleteBackendRenderTarget)); return reinterpret_cast<KPointer>(reinterpret_cast<uintptr_t>(&deleteBackendRenderTarget));
} }
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "SkLumaColorFilter.h" #include "SkLumaColorFilter.h"
#include "SkOverdrawColorFilter.h" #include "SkOverdrawColorFilter.h"
#include "SkTableColorFilter.h" #include "SkTableColorFilter.h"
#include "common.h" #include "common.h"
#include <emscripten.h> #include <emscripten.h>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <emscripten.h> #include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
extern "C" void org_jetbrains_skia_ColorSpace__nConvert( extern "C" void org_jetbrains_skia_ColorSpace__nConvert(
KPointer fromPtr, KPointer toPtr, float r, float g, float b, float a, float* result) { KPointer fromPtr, KPointer toPtr, float r, float g, float b, float a, float* result) {
SkColorSpace* from = reinterpret_cast<SkColorSpace*>(fromPtr); SkColorSpace* from = reinterpret_cast<SkColorSpace*>(fromPtr);
......
#include "common.h"
#include <emscripten.h>
typedef void (*finalizer_t)(void*);
EMSCRIPTEN_KEEPALIVE
extern "C" void org_jetbrains_skia_ColorSpace__nInvokeFinalizer(KPointer finalizer, KPointer obj) {
finalizer_t finalizer_f = reinterpret_cast<finalizer_t>(finalizer);
finalizer_f(obj);
}
\ No newline at end of file
#include <emscripten.h>
#include "common.h"
#include "SkRefCnt.h"
class SkRefCntHack {
public:
void* x;
mutable std::atomic<int32_t> fRefCnt;
};
void unrefSkRefCnt(SkRefCnt* p) {
p->unref();
}
EMSCRIPTEN_KEEPALIVE
extern "C" KPointer org_jetbrains_skia_impl_RefCnt__1nGetFinalizer() {
return reinterpret_cast<KPointer>(&unrefSkRefCnt);
}
EMSCRIPTEN_KEEPALIVE
extern "C" KInt org_jetbrains_skia_impl_RefCnt__1nGetRefCount(KPointer ptr) {
SkRefCnt* instance = reinterpret_cast<SkRefCnt*>(ptr);
return reinterpret_cast<SkRefCntHack*>(instance)->fRefCnt.load(std::memory_order_relaxed);
}
#pragma once #pragma once
#include <stdint.h>
typedef int32_t KInt; typedef int32_t KInt;
typedef int64_t KLong; typedef int64_t KLong;
typedef int32_t KPointer; typedef void* KPointer;
typedef uint8_t* KByteArray; typedef uint8_t* KByteArray;
...@@ -3,6 +3,7 @@ package org.jetbrains.skia.impl ...@@ -3,6 +3,7 @@ package org.jetbrains.skia.impl
actual class Library { actual class Library {
actual companion object { actual companion object {
actual fun staticLoad() { actual fun staticLoad() {
// TODO: load wasm and js modules, maybe just js, as it will load Wasm.
TODO() TODO()
} }
} }
......
package org.jetbrains.skia.impl package org.jetbrains.skia.impl
private class FinalizationThunk(private val finalizer: NativePointer, private var obj: NativePointer) {
fun clean() {
if (obj != 0)
_nInvokeFinalizer(finalizer, obj)
obj = 0
}
}
external class FinalizationRegistry(cleanup: (dynamic) -> Unit) {
fun register(obj: dynamic, handle: dynamic)
fun unregister(obj: dynamic)
}
private val registry = FinalizationRegistry {
val thunk = it as FinalizationThunk
thunk.clean()
}
private fun register(managed: Managed, thunk: FinalizationThunk) {
registry.register(managed, thunk)
}
private fun unregister(managed: Managed) {
registry.unregister(managed)
}
actual abstract class Managed actual constructor(ptr: NativePointer, finalizer: NativePointer, managed: Boolean) : Native(ptr) { actual abstract class Managed actual constructor(ptr: NativePointer, finalizer: NativePointer, managed: Boolean) : Native(ptr) {
actual open fun close(): Unit = TODO() private var cleaner: FinalizationThunk? = null
actual open fun close() {
if (NullPointer == _ptr)
throw RuntimeException("Object already closed: ${this::class.simpleName}, _ptr=$_ptr")
else if (null == cleaner)
throw RuntimeException("Object is not managed, can't close(): ${this::class.simpleName}, _ptr=$_ptr")
else {
unregister(this)
cleaner!!.clean()
cleaner = null
_ptr = 0
}
}
actual open val isClosed: Boolean actual open val isClosed: Boolean
get() = _ptr == NullPointer get() = _ptr == NullPointer
init {
if (managed) {
require(ptr != 0) { "Managed ptr is 0" }
require(finalizer != 0) { "Managed finalizer is 0" }
val thunk = FinalizationThunk(ptr, finalizer)
register(this, thunk)
cleaner = thunk
}
}
} }
@JsName("org_jetbrains_skia_Managed__invokeFinalizer")
private external fun _nInvokeFinalizer(finalizer: NativePointer, obj: NativePointer)
\ No newline at end of file
package org.jetbrains.skia.impl package org.jetbrains.skia.impl
actual abstract class RefCnt : Managed { actual abstract class RefCnt : Managed {
actual protected constructor(ptr: NativePointer): super(ptr, NullPointer, false) { TODO() } actual protected constructor(ptr: NativePointer): super(ptr, _FinalizerHolder.PTR, false)
actual protected constructor(ptr: NativePointer, allowClose: Boolean): super(ptr, 0, allowClose) { TODO() } actual protected constructor(ptr: NativePointer, allowClose: Boolean): super(ptr, _FinalizerHolder.PTR, allowClose)
val refCount: Int
get() {
Stats.onNativeCall()
return _nGetRefCount(_ptr)
}
override fun toString(): String {
val s = super.toString()
return s.substring(0, s.length - 1) + ", refCount=" + refCount + ")"
}
} }
private object _FinalizerHolder {
val PTR = _nGetFinalizer()
}
@JsName("org_jetbrains_skia_RefCnt__nGetFinalizer")
private external fun _nGetFinalizer(): NativePointer
@JsName("org_jetbrains_skia_RefCnt__nGetCount")
private external fun _nGetRefCount(ptr: NativePointer): Int
package org.jetbrains.skia.impl package org.jetbrains.skia.impl
actual object Stats { actual object Stats {
actual fun onNativeCall() { actual fun onNativeCall() {}
TODO()
}
actual fun onAllocated(className: String) { actual fun onAllocated(className: String) {}
TODO()
}
actual fun onDeallocated(className: String) { actual fun onDeallocated(className: String) {}
TODO()
}
} }
\ No newline at end of file
...@@ -6,9 +6,11 @@ actual abstract class Managed actual constructor(ptr: Long, finalizer: Long, man ...@@ -6,9 +6,11 @@ actual abstract class Managed actual constructor(ptr: Long, finalizer: Long, man
AutoCloseable { AutoCloseable {
private var _cleanable: Cleaner.Cleanable? = null private var _cleanable: Cleaner.Cleanable? = null
actual override fun close() { actual override fun close() {
if (0L == _ptr) throw RuntimeException("Object already closed: $javaClass, _ptr=$_ptr") else if (null == _cleanable) throw RuntimeException( if (0L == _ptr)
"Object is not managed in JVM, can't close(): $javaClass, _ptr=$_ptr" throw RuntimeException("Object already closed: $javaClass, _ptr=$_ptr")
) else { else if (null == _cleanable)
throw RuntimeException("Object is not managed in JVM, can't close(): $javaClass, _ptr=$_ptr")
else {
_cleanable!!.clean() _cleanable!!.clean()
_cleanable = null _cleanable = null
_ptr = 0 _ptr = 0
......
...@@ -3,7 +3,7 @@ package org.jetbrains.skia.impl ...@@ -3,7 +3,7 @@ package org.jetbrains.skia.impl
import java.lang.ref.Reference import java.lang.ref.Reference
actual abstract class RefCnt : Managed { actual abstract class RefCnt : Managed {
protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR) {} protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR)
protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose) protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose)
val refCount: Int val refCount: Int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment