[MERGE] KT: build-1.5.0-dev-1616 KT/N: abfbfcde4 OLD: 8bb76c67e
This commit is contained in:
+1
-1
@@ -10,6 +10,6 @@ import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.konan.file.*
|
||||
|
||||
fun produceCAdapterBitcode(clang: ClangArgs, cppFileName: String, bitcodeFileName: String) {
|
||||
val clangCommand = clang.clangCXX("-std=c++14", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName)
|
||||
val clangCommand = clang.clangCXX("-std=c++17", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName)
|
||||
Command(clangCommand).execute()
|
||||
}
|
||||
|
||||
+23
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.LinkerOutputKind
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal fun TypeBridge.makeNothing() = when (this) {
|
||||
@@ -274,6 +275,8 @@ internal class ObjCExportCodeGenerator(
|
||||
emitSelectorsHolder()
|
||||
|
||||
emitStaticInitializers()
|
||||
|
||||
emitKt42254Hint()
|
||||
}
|
||||
|
||||
private fun emitTypeAdapters() {
|
||||
@@ -345,6 +348,24 @@ internal class ObjCExportCodeGenerator(
|
||||
context.llvm.otherStaticInitializers += initializer
|
||||
}
|
||||
|
||||
private fun emitKt42254Hint() {
|
||||
if (determineLinkerOutput(context) == LinkerOutputKind.STATIC_LIBRARY) {
|
||||
// Might be affected by https://youtrack.jetbrains.com/issue/KT-42254.
|
||||
// The code below generally follows [replaceExternalWeakOrCommonGlobal] implementation.
|
||||
if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherObjectFiles()) {
|
||||
// So the compiler uses caches. If a user is linking two such static frameworks into a single binary,
|
||||
// the linker might fail with a lot of "duplicate symbol" errors due to KT-42254.
|
||||
// Adding a similar symbol that would explicitly hint to take a look at the YouTrack issue if reported.
|
||||
// Note: for some reason this symbol is reported as the last one, which is good for its purpose.
|
||||
val name = "See https://youtrack.jetbrains.com/issue/KT-42254"
|
||||
val global = staticData.placeGlobal(name, Int8(0), isExported = true)
|
||||
|
||||
context.llvm.usedGlobals += global.llvmGlobal
|
||||
LLVMSetVisibility(global.llvmGlobal, LLVMVisibility.LLVMHiddenVisibility)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider including this into ObjCExportCodeSpec.
|
||||
private val objCClassForAny = ObjCClassForKotlinClass(
|
||||
namer.kotlinAnyName.binaryName,
|
||||
@@ -488,6 +509,8 @@ private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal(
|
||||
// but it is simpler to do this for all globals, considering that all usages can't be removed by DCE anyway.
|
||||
context.llvm.usedGlobals += global.llvmGlobal
|
||||
LLVMSetVisibility(global.llvmGlobal, LLVMVisibility.LLVMHiddenVisibility)
|
||||
|
||||
// See also [emitKt42254Hint].
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-18
@@ -5,35 +5,46 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.renderCompilerError
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
|
||||
/**
|
||||
* This pass runs after inlining and performs the following additional transformations over some operations:
|
||||
* - Convert immutableBlobOf() arguments to special IrConst.
|
||||
* - Convert `obj::class` and `Class::class` to calls.
|
||||
*/
|
||||
internal class PostInlineLowering(val context: Context) : FileLoweringPass {
|
||||
internal class PostInlineLowering(val context: Context) : BodyLoweringPass {
|
||||
|
||||
private val symbols get() = context.ir.symbols
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrBuildingTransformer(context) {
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
val irFile = container.file
|
||||
irBody.transformChildren(object : IrElementTransformer<IrBuilderWithScope> {
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrBuilderWithScope) =
|
||||
super.visitDeclaration(declaration,
|
||||
data = (declaration as? IrSymbolOwner)?.let { context.createIrBuilder(it.symbol, it.startOffset, it.endOffset) }
|
||||
?: data
|
||||
)
|
||||
|
||||
override fun visitClassReference(expression: IrClassReference): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
override fun visitClassReference(expression: IrClassReference, data: IrBuilderWithScope): IrExpression {
|
||||
expression.transformChildren(this, data)
|
||||
|
||||
return builder.at(expression).run {
|
||||
return data.at(expression).run {
|
||||
(expression.symbol as? IrClassSymbol)?.let { irKClass(this@PostInlineLowering.context, it) }
|
||||
?:
|
||||
// E.g. for `T::class` in a body of an inline function itself.
|
||||
@@ -41,10 +52,10 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetClass(expression: IrGetClass): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
override fun visitGetClass(expression: IrGetClass, data: IrBuilderWithScope): IrExpression {
|
||||
expression.transformChildren(this, data)
|
||||
|
||||
return builder.at(expression).run {
|
||||
return data.at(expression).run {
|
||||
irCall(symbols.kClassImplConstructor, listOf(expression.argument.type)).apply {
|
||||
val typeInfo = irCall(symbols.getObjectTypeInfo).apply {
|
||||
putValueArgument(0, expression.argument)
|
||||
@@ -55,8 +66,8 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
override fun visitCall(expression: IrCall, data: IrBuilderWithScope): IrExpression {
|
||||
expression.transformChildren(this, data)
|
||||
|
||||
// Function inlining is changing function symbol at callsite
|
||||
// and unbound symbol replacement is happening later.
|
||||
@@ -83,13 +94,16 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
|
||||
context.irBuiltIns.stringType,
|
||||
IrConstKind.String, builder.toString()))
|
||||
} else if (Symbols.isTypeOfIntrinsic(expression.symbol)) {
|
||||
return with (KTypeGenerator(context, irFile, expression, needExactTypeParameters = true)) {
|
||||
builder.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = false)
|
||||
// Inline functions themselves are not called (they have been inlined at all call sites),
|
||||
// so it is ok not to build exact type parameters for them.
|
||||
val needExactTypeParameters = (container as? IrSimpleFunction)?.isInline != true
|
||||
return with (KTypeGenerator(context, irFile, expression, needExactTypeParameters)) {
|
||||
data.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = false)
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
})
|
||||
}, data = context.createIrBuilder((container as IrSymbolOwner).symbol, irBody.startOffset, irBody.endOffset))
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -567,7 +567,8 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme
|
||||
if (type !is IrSimpleType)
|
||||
return
|
||||
val classifier = type.classifier
|
||||
if (classifier is IrTypeParameterSymbol)
|
||||
if (classifier is IrTypeParameterSymbol
|
||||
&& !classifier.owner.isReified /* Reified may be substituted with valid types later */)
|
||||
checkIrKTypeParameter(irElement, classifier.owner, seenTypeParameters)
|
||||
|
||||
type.arguments.forEach {
|
||||
|
||||
+2
-1
@@ -265,7 +265,8 @@ private fun ObjCExportMapper.bridgeReturnType(
|
||||
}
|
||||
}
|
||||
|
||||
descriptor.containingDeclaration == descriptor.builtIns.any && descriptor.name.asString() == "hashCode" -> {
|
||||
descriptor.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) } &&
|
||||
descriptor.name.asString() == "hashCode" -> {
|
||||
assert(!convertExceptionsToErrors)
|
||||
MethodBridge.ReturnValue.HashCode
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ linker = clang++
|
||||
linkerOpts = -fvisibility-inlines-hidden \
|
||||
-Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers \
|
||||
-pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor \
|
||||
-std=c++14 \
|
||||
-std=c++17 \
|
||||
-DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \
|
||||
-ldebugInfo -lcoverageMapping
|
||||
|
||||
|
||||
@@ -3723,7 +3723,7 @@ createInterop("concurrentTerminate") {
|
||||
it.headers "$projectDir/interop/concurrentTerminate/async.h"
|
||||
// TODO: Using `-Xcompile-source` does not imply dependency on that source, so the task will no re-run when the source is updated.
|
||||
it.extraOpts "-Xcompile-source", "$projectDir/interop/concurrentTerminate/async.cpp"
|
||||
it.extraOpts "-Xsource-compiler-option", "-std=c++11"
|
||||
it.extraOpts "-Xsource-compiler-option", "-std=c++17"
|
||||
}
|
||||
|
||||
createInterop("incomplete_types") {
|
||||
|
||||
@@ -76,3 +76,13 @@ fun test_reifiedUpperBound() {
|
||||
assertTrue((t as KTypeParameter).isReified)
|
||||
assertEquals("T", (t as KTypeParameter).name)
|
||||
}
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
inline fun <reified T : Comparable<T>> recursionInReified() = typeOf<List<T>>()
|
||||
|
||||
@Test
|
||||
fun test_recursionInReified() {
|
||||
val l = recursionInReified<Int>()
|
||||
assertEquals(List::class, l.classifier)
|
||||
assertEquals(Int::class, l.arguments.single().type!!.classifier)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import kotlin.reflect.*
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
inline fun <reified T : Comparable<T>> foo() {
|
||||
inline fun <T : Comparable<T>> foo() {
|
||||
typeOf<List<T>>()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,22 @@ class ExecClang(private val project: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveToolchainExecutable(target: KonanTarget, executableOrNull: String?): String {
|
||||
val executable = executableOrNull ?: "clang"
|
||||
|
||||
if (listOf("clang", "clang++").contains(executable)) {
|
||||
// TODO: This is copied from `BitcodeCompiler`. Consider sharing the code instead.
|
||||
val platform = platformManager.platform(target)
|
||||
return if (target.family.isAppleFamily) {
|
||||
"${platform.absoluteTargetToolchain}/usr/bin/$executable"
|
||||
} else {
|
||||
"${platform.absoluteTargetToolchain}/bin/$executable"
|
||||
}
|
||||
} else {
|
||||
throw GradleException("unsupported clang executable: $executable")
|
||||
}
|
||||
}
|
||||
|
||||
// The bare ones invoke clang with system default sysroot.
|
||||
|
||||
fun execBareClang(action: Action<in ExecSpec>): ExecResult {
|
||||
@@ -81,6 +97,30 @@ class ExecClang(private val project: Project) {
|
||||
return this.execClang(konanArgs(target), closure)
|
||||
}
|
||||
|
||||
// The toolchain ones execute clang from the toolchain.
|
||||
|
||||
fun execToolchainClang(target: String?, action: Action<in ExecSpec>): ExecResult {
|
||||
return this.execToolchainClang(platformManager.targetManager(target).target, action)
|
||||
}
|
||||
|
||||
fun execToolchainClang(target: String?, closure: Closure<in ExecSpec>): ExecResult {
|
||||
return this.execToolchainClang(platformManager.targetManager(target).target, ConfigureUtil.configureUsing(closure))
|
||||
}
|
||||
|
||||
fun execToolchainClang(target: KonanTarget, action: Action<in ExecSpec>): ExecResult {
|
||||
val extendedAction = Action<ExecSpec> { execSpec ->
|
||||
action.execute(execSpec)
|
||||
execSpec.apply {
|
||||
executable = resolveToolchainExecutable(target, executable)
|
||||
}
|
||||
}
|
||||
return project.exec(extendedAction)
|
||||
}
|
||||
|
||||
fun execToolchainClang(target: KonanTarget, closure: Closure<in ExecSpec>): ExecResult {
|
||||
return this.execToolchainClang(target, ConfigureUtil.configureUsing(closure))
|
||||
}
|
||||
|
||||
// These ones are private, so one has to choose either Bare or Konan.
|
||||
|
||||
private fun execClang(defaultArgs: List<String>, closure: Closure<in ExecSpec>): ExecResult {
|
||||
|
||||
+2
-3
@@ -68,10 +68,9 @@ open class CompileToBitcode @Inject constructor(
|
||||
// Used flags provided by original build of allocator C code.
|
||||
listOf("-std=gnu11", "-O3", "-Wall", "-Wextra", "-Werror")
|
||||
Language.CPP ->
|
||||
listOfNotNull("-std=c++14", "-Werror", "-O2",
|
||||
listOfNotNull("-std=c++17", "-Werror", "-O2",
|
||||
"-Wall", "-Wextra",
|
||||
"-Wno-unused-parameter", // False positives with polymorphic functions.
|
||||
"-Wno-unused-function", // TODO: Enable this warning when we have C++ runtime tests.
|
||||
"-fPIC".takeIf { !HostManager().targetByName(target).isMINGW })
|
||||
}
|
||||
return commonFlags + languageFlags + compilerArgs
|
||||
@@ -151,4 +150,4 @@ open class CompileToBitcode @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -17,10 +17,10 @@ import org.jetbrains.kotlin.konan.target.*
|
||||
|
||||
open class CompileNativeTest @Inject constructor(
|
||||
@InputFile val inputFile: File,
|
||||
@Input val target: String
|
||||
@Input val target: KonanTarget,
|
||||
) : DefaultTask() {
|
||||
@OutputFile
|
||||
var outputFile = project.buildDir.resolve("bin/test/$target/${inputFile.nameWithoutExtension}.o")
|
||||
var outputFile = project.buildDir.resolve("bin/test/${target.name}/${inputFile.nameWithoutExtension}.o")
|
||||
|
||||
@Input
|
||||
val clangArgs = mutableListOf<String>()
|
||||
@@ -28,9 +28,16 @@ open class CompileNativeTest @Inject constructor(
|
||||
@TaskAction
|
||||
fun compile() {
|
||||
val plugin = project.convention.getPlugin(ExecClang::class.java)
|
||||
plugin.execBareClang {
|
||||
it.executable = "clang++"
|
||||
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
||||
if (target.family.isAppleFamily) {
|
||||
plugin.execToolchainClang(target) {
|
||||
it.executable = "clang++"
|
||||
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
||||
}
|
||||
} else {
|
||||
plugin.execBareClang {
|
||||
it.executable = "clang++"
|
||||
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +225,7 @@ fun createTestTask(
|
||||
"${testTaskName}Compile",
|
||||
CompileNativeTest::class.java,
|
||||
llvmLinkTask.outputFile,
|
||||
target
|
||||
konanTarget,
|
||||
).apply {
|
||||
dependsOn(llvmLinkTask)
|
||||
clangArgs.addAll(clangFlags.clangFlags)
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
DIR="${BASH_SOURCE[0]%/*}"
|
||||
: ${DIR:="."}
|
||||
|
||||
"${DIR}"/run_konan konanc "$@"
|
||||
"${DIR}"/run_konan kotlinc "$@"
|
||||
|
||||
@@ -14,4 +14,4 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
rem See the License for the specific language governing permissions and
|
||||
rem limitations under the License.
|
||||
|
||||
call %~dps0run_konan.bat konanc %*
|
||||
call %~dps0run_konan.bat kotlinc %*
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
* Put implementation details inside `.h`/`.hpp` into a nested `namespace internal` (e.g. implementation details of module `mm` go into `namespace kotlin { namespace mm { namespace internal { ... } } }`)
|
||||
* Put implementation details inside `.cpp`/`.mm` into a global anonymous `namespace`
|
||||
* For `extern "C"` declarations emulate namespaces with `Kotlin_[module_name]_` prefixes.
|
||||
* To mark type as move-only, privately inherit from `kotlin::MoveOnly`
|
||||
* To mark type unmovable and uncopyable, privately inherit from `kotlin::Pinned`
|
||||
* All heap-allocated classes should publicly inherit from `KonanAllocatorAware`
|
||||
* Use `KStd*` containers and smart pointers instead of `std::*` ones.
|
||||
|
||||
## Naming
|
||||
|
||||
|
||||
@@ -57,11 +57,6 @@ uint32_t Fetch32(const char *p) {
|
||||
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
|
||||
}
|
||||
|
||||
uint32_t Rotate32(uint32_t val, int shift) {
|
||||
// Avoid shifting by 32: doing so yields an undefined result.
|
||||
return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
|
||||
}
|
||||
|
||||
// Bitwise right rotate. Normally this will compile to a single
|
||||
// instruction, especially if the shift is a manifest constant.
|
||||
uint64_t Rotate(uint64_t val, int shift) {
|
||||
|
||||
@@ -23,18 +23,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t PrintableHexSize(uint32_t input_length) {
|
||||
return input_length * 2;
|
||||
}
|
||||
|
||||
void PrintableHex(const uint8_t* data, uint32_t data_length, char* hex) {
|
||||
static const char* hex_digits = "0123456789ABCDEF";
|
||||
for (uint32_t i = 0; i < data_length; ++i) {
|
||||
*hex++ = hex_digits[(*data >> 4) & 0xf];
|
||||
*hex++ = hex_digits[(*data++) & 0xf];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t PrintableBase64Size(uint32_t input_length) {
|
||||
return ((input_length + 2) / 3 * 4) + 1;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
buildKotlinVersion=1.4.20-dev-2167
|
||||
buildKotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.4.20-dev-2167,branch:default:any,pinned:true/artifacts/content/maven
|
||||
remoteRoot=konan_tests
|
||||
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.0-dev-1282,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinVersion=1.5.0-dev-1282
|
||||
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.0-dev-1282,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinStdlibVersion=1.5.0-dev-1282
|
||||
kotlinStdlibTestsVersion=1.5.0-dev-1282
|
||||
testKotlinCompilerVersion=1.5.0-dev-1282
|
||||
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.0-dev-1616,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinVersion=1.5.0-dev-1616
|
||||
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.0-dev-1616,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinStdlibVersion=1.5.0-dev-1616
|
||||
kotlinStdlibTestsVersion=1.5.0-dev-1616
|
||||
testKotlinCompilerVersion=1.5.0-dev-1616
|
||||
konanVersion=1.5.0
|
||||
|
||||
# A version of Xcode required to build the Kotlin/Native compiler.
|
||||
|
||||
@@ -42,7 +42,7 @@ model {
|
||||
binaries.withType(StaticLibraryBinarySpec) { binary ->
|
||||
if (!project.parent.convention.plugins.platformInfo.isWindows())
|
||||
cppCompiler.args "-fPIC"
|
||||
cppCompiler.args "--std=c++11", "-g", "-I${llvmDir}/include"
|
||||
cppCompiler.args "--std=c++17", "-g", "-I${llvmDir}/include"
|
||||
if (isEnabled) {
|
||||
cppCompiler.args '-DLIBCLANGEXT_ENABLE=1'
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ model {
|
||||
binaries.withType(StaticLibraryBinarySpec) { binary ->
|
||||
if (!project.parent.convention.plugins.platformInfo.isWindows())
|
||||
cppCompiler.args "-fPIC"
|
||||
cppCompiler.args "--std=c++14", "-I${llvmDir}/include", "-I${projectDir}/src/main/include"
|
||||
cppCompiler.args "--std=c++17", "-I${llvmDir}/include", "-I${projectDir}/src/main/include"
|
||||
if (isMac()) {
|
||||
cppCompiler.args "-DKONAN_MACOS=1"
|
||||
} else if (isWindows()) {
|
||||
|
||||
@@ -37,7 +37,7 @@ model {
|
||||
binaries.withType(StaticLibraryBinarySpec) { binary ->
|
||||
if (!project.parent.convention.plugins.platformInfo.isWindows())
|
||||
cppCompiler.args "-fPIC"
|
||||
cppCompiler.args "--std=c++14", "-I${llvmDir}/include", "-I${projectDir}/src/main/include"
|
||||
cppCompiler.args "--std=c++17", "-I${llvmDir}/include", "-I${projectDir}/src/main/include"
|
||||
linker.args "-L${llvmDir}/lib", "-lLLVMCore", "-lLLVMSupport"
|
||||
}
|
||||
binaries.withType(SharedLibraryBinarySpec) { binary ->
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <llvm-c/DebugInfo.h>
|
||||
#include "DebugInfoC.h"
|
||||
/**
|
||||
* c++ --std=c++14 llvmDebugInfoC/src/DebugInfoC.cpp -IllvmDebugInfoC/include/ -Idependencies/all/clang+llvm-3.9.0-darwin-macos/include -Ldependencies/all/clang+llvm-3.9.0-darwin-macos/lib -lLLVMCore -lLLVMSupport -lncurses -shared -o libLLVMDebugInfoC.dylib
|
||||
* c++ --std=c++17 llvmDebugInfoC/src/DebugInfoC.cpp -IllvmDebugInfoC/include/ -Idependencies/all/clang+llvm-3.9.0-darwin-macos/include -Ldependencies/all/clang+llvm-3.9.0-darwin-macos/lib -lLLVMCore -lLLVMSupport -lncurses -shared -o libLLVMDebugInfoC.dylib
|
||||
*/
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@@ -129,10 +129,10 @@ class CyclicCollector {
|
||||
|
||||
public:
|
||||
CyclicCollector() {
|
||||
CHECK_CALL(pthread_mutex_init(&lock_, nullptr), "Cannot init collector mutex")
|
||||
CHECK_CALL(pthread_mutex_init(×tampLock_, nullptr), "Cannot init collector timestamp mutex")
|
||||
CHECK_CALL(pthread_cond_init(&cond_, nullptr), "Cannot init collector condition")
|
||||
CHECK_CALL(pthread_create(&gcThread_, nullptr, gcWorkerRoutine, this), "Cannot start collector thread")
|
||||
CHECK_CALL(pthread_mutex_init(&lock_, nullptr), "Cannot init collector mutex");
|
||||
CHECK_CALL(pthread_mutex_init(×tampLock_, nullptr), "Cannot init collector timestamp mutex");
|
||||
CHECK_CALL(pthread_cond_init(&cond_, nullptr), "Cannot init collector condition");
|
||||
CHECK_CALL(pthread_create(&gcThread_, nullptr, gcWorkerRoutine, this), "Cannot start collector thread");
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@@ -146,7 +146,7 @@ class CyclicCollector {
|
||||
Locker locker(&lock_);
|
||||
terminateCollector_ = true;
|
||||
if (enabled) shallRunCollector_ = true;
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
|
||||
}
|
||||
// TODO: improve waiting for collector termination.
|
||||
while (atomicGet(&terminateCollector_)) {}
|
||||
@@ -173,7 +173,7 @@ class CyclicCollector {
|
||||
KStdUnorderedMap<ObjHeader*, int> sideRefCounts;
|
||||
int restartCount = 0;
|
||||
while (!terminateCollector_) {
|
||||
CHECK_CALL(pthread_cond_wait(&cond_, &lock_), "Cannot wait collector condition")
|
||||
CHECK_CALL(pthread_cond_wait(&cond_, &lock_), "Cannot wait collector condition");
|
||||
if (!shallRunCollector_) continue;
|
||||
atomicSet(&gcRunning_, 1);
|
||||
restartCount = 0;
|
||||
@@ -325,7 +325,7 @@ class CyclicCollector {
|
||||
// When exiting the worker - we shall collect the cyclic garbage here.
|
||||
if (enabled) {
|
||||
shallRunCollector_ = true;
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
|
||||
}
|
||||
currentAliveWorkers_--;
|
||||
}
|
||||
@@ -416,7 +416,7 @@ class CyclicCollector {
|
||||
if (checkIfShallCollect()) {
|
||||
Locker locker(&lock_);
|
||||
shallRunCollector_ = true;
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +424,7 @@ class CyclicCollector {
|
||||
if (atomicGet(&gcRunning_) != 0) return;
|
||||
Locker lock(&lock_);
|
||||
shallRunCollector_ = true;
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
|
||||
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector");
|
||||
}
|
||||
|
||||
void localGC() {
|
||||
|
||||
@@ -192,7 +192,7 @@ struct CycleDetectorRootset {
|
||||
KStdVector<ScopedRefHolder> heldRefs;
|
||||
};
|
||||
|
||||
class CycleDetector : private kotlin::Pinned {
|
||||
class CycleDetector : private kotlin::Pinned, public KonanAllocatorAware {
|
||||
public:
|
||||
static void insertCandidateIfNeeded(KRef object) {
|
||||
if (canBeACandidate(object))
|
||||
@@ -798,7 +798,6 @@ namespace {
|
||||
void freeContainer(ContainerHeader* header) NO_INLINE;
|
||||
#if USE_GC
|
||||
void garbageCollect(MemoryState* state, bool force) NO_INLINE;
|
||||
void cyclicGarbageCollect() NO_INLINE;
|
||||
void rememberNewContainer(ContainerHeader* container);
|
||||
#endif // USE_GC
|
||||
|
||||
@@ -1017,10 +1016,6 @@ inline FrameOverlay* asFrameOverlay(ObjHeader** slot) {
|
||||
return reinterpret_cast<FrameOverlay*>(slot);
|
||||
}
|
||||
|
||||
inline bool isRefCounted(KConstRef object) {
|
||||
return isFreeable(containerFor(object));
|
||||
}
|
||||
|
||||
inline void lock(KInt* spinlock) {
|
||||
while (compareAndSwap(spinlock, 0, 1) != 0) {}
|
||||
}
|
||||
@@ -1169,7 +1164,7 @@ void freeAggregatingFrozenContainer(ContainerHeader* container) {
|
||||
auto* state = memoryState;
|
||||
RuntimeAssert(isAggregatingFrozenContainer(container), "expected fictitious frozen container");
|
||||
MEMORY_LOG("%p is fictitious frozen container\n", container);
|
||||
RuntimeAssert(!container->buffered(), "frozen objects must not participate in GC")
|
||||
RuntimeAssert(!container->buffered(), "frozen objects must not participate in GC");
|
||||
#if USE_GC
|
||||
// Forbid finalizerQueue handling.
|
||||
++state->finalizerQueueSuspendCount;
|
||||
@@ -1698,6 +1693,7 @@ void collectWhite(MemoryState* state, ContainerHeader* start) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if COLLECT_STATISTIC
|
||||
inline bool needAtomicAccess(ContainerHeader* container) {
|
||||
return container->shareable();
|
||||
}
|
||||
@@ -1707,6 +1703,7 @@ inline bool canBeCyclic(ContainerHeader* container) {
|
||||
if (container->color() == CONTAINER_TAG_GC_GREEN) return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void addHeapRef(ContainerHeader* container) {
|
||||
MEMORY_LOG("AddHeapRef %p: rc=%d\n", container, container->refCount())
|
||||
@@ -1774,6 +1771,11 @@ inline void releaseHeapRef(const ObjHeader* header) {
|
||||
releaseHeapRef<Strict, CanCollect>(const_cast<ContainerHeader*>(container));
|
||||
}
|
||||
|
||||
|
||||
// TODO: Consider removing this unused stuff.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
// We use first slot as place to store frame-local arena container.
|
||||
// TODO: create ArenaContainer object on the stack, so that we don't
|
||||
// do two allocations per frame (ArenaContainer + actual container).
|
||||
@@ -1800,6 +1802,8 @@ inline size_t containerSize(const ContainerHeader* container) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#if USE_GC
|
||||
void incrementStack(MemoryState* state) {
|
||||
FrameOverlay* frame = currentFrame;
|
||||
@@ -2052,7 +2056,7 @@ MemoryState* initMemory(bool firstRuntime) {
|
||||
==
|
||||
offsetof(MetaObjHeader, typeInfo_),
|
||||
"Layout mismatch");
|
||||
RuntimeAssert(sizeof(FrameOverlay) % sizeof(ObjHeader**) == 0, "Frame overlay should contain only pointers")
|
||||
RuntimeAssert(sizeof(FrameOverlay) % sizeof(ObjHeader**) == 0, "Frame overlay should contain only pointers");
|
||||
RuntimeAssert(memoryState == nullptr, "memory state must be clear");
|
||||
memoryState = konanConstructInstance<MemoryState>();
|
||||
INIT_EVENT(memoryState)
|
||||
|
||||
@@ -119,4 +119,42 @@ bool operator!=(
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class KonanDeleter {
|
||||
public:
|
||||
void operator()(T* instance) noexcept { konanDestructInstance(instance); }
|
||||
};
|
||||
|
||||
// Force a class to be heap-allocated using `konanAllocMemory`. Does not prevent stack allocation, or
|
||||
// allocation as part of another object.
|
||||
// Usage:
|
||||
// class A : public KonanAllocatorAware {
|
||||
// ...
|
||||
// };
|
||||
class KonanAllocatorAware {
|
||||
public:
|
||||
static void* operator new(size_t count) noexcept { return konanAllocMemory(count); }
|
||||
static void* operator new[](size_t count) noexcept { return konanAllocMemory(count); }
|
||||
|
||||
static void* operator new(size_t count, void* ptr) noexcept { return ptr; }
|
||||
static void* operator new[](size_t count, void* ptr) noexcept { return ptr; }
|
||||
|
||||
static void operator delete(void* ptr) noexcept { konanFreeMemory(ptr); }
|
||||
static void operator delete[](void* ptr) noexcept { konanFreeMemory(ptr); }
|
||||
|
||||
protected:
|
||||
// Hide constructors, assignments and destructor to discourage operating on instance of `KonanAllocatorAware`
|
||||
KonanAllocatorAware() = default;
|
||||
|
||||
KonanAllocatorAware(const KonanAllocatorAware&) = default;
|
||||
KonanAllocatorAware(KonanAllocatorAware&&) = default;
|
||||
|
||||
KonanAllocatorAware& operator=(const KonanAllocatorAware&) = default;
|
||||
KonanAllocatorAware& operator=(KonanAllocatorAware&&) = default;
|
||||
|
||||
// Not virtual by design. Since this class hides this destructor, no one can destroy an
|
||||
// instance of `KonanAllocatorAware` directly, so this destructor is never called in a virtual manner.
|
||||
~KonanAllocatorAware() = default;
|
||||
};
|
||||
|
||||
#endif // RUNTIME_ALLOC_H
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "Alloc.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class A : public KonanAllocatorAware {
|
||||
public:
|
||||
using DestructorHook = testing::StrictMock<testing::MockFunction<void(int)>>;
|
||||
|
||||
static thread_local DestructorHook* destructorHook;
|
||||
|
||||
explicit A(int value = -1) : value_(value) {}
|
||||
|
||||
~A() { destructorHook->Call(value_); }
|
||||
|
||||
int value() const { return value_; }
|
||||
|
||||
bool operator==(const A& rhs) const { return value_ == rhs.value_; }
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
|
||||
// static
|
||||
thread_local A::DestructorHook* A::destructorHook = nullptr;
|
||||
|
||||
struct B {
|
||||
explicit B(int value) : a(value) {}
|
||||
|
||||
A a;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class KonanAllocatorAwareTest : public testing::Test {
|
||||
public:
|
||||
KStdUniquePtr<A::DestructorHook> destructorHook;
|
||||
|
||||
void SetUp() override {
|
||||
Test::SetUp();
|
||||
|
||||
destructorHook = make_unique<A::DestructorHook>();
|
||||
A::destructorHook = destructorHook.get();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
A::destructorHook = nullptr;
|
||||
destructorHook.reset();
|
||||
|
||||
Test::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, AllocatedOnStack) {
|
||||
A a(42);
|
||||
EXPECT_THAT(a.value(), 42);
|
||||
EXPECT_CALL(*destructorHook, Call(42));
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, AllocatedInAnotherObject) {
|
||||
// We do not control how `B` is allocated.
|
||||
B* b = new B(42);
|
||||
EXPECT_THAT(b->a.value(), 42);
|
||||
EXPECT_CALL(*destructorHook, Call(42));
|
||||
delete b;
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, AllocatedByItself) {
|
||||
A* a = new A(42);
|
||||
EXPECT_THAT(a->value(), 42);
|
||||
EXPECT_CALL(*destructorHook, Call(42));
|
||||
delete a;
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, AllocateArray) {
|
||||
constexpr size_t kCount = 5;
|
||||
A* as = new A[kCount];
|
||||
|
||||
std::vector<int> actual;
|
||||
for (A* a = as; a != as + kCount; ++a) {
|
||||
actual.push_back(a->value());
|
||||
}
|
||||
std::array<int, kCount> expected;
|
||||
for (int& element : expected) {
|
||||
element = -1;
|
||||
}
|
||||
EXPECT_THAT(actual, testing::ElementsAreArray(expected));
|
||||
|
||||
EXPECT_CALL(*destructorHook, Call(-1)).Times(kCount);
|
||||
delete[] as;
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, PlacementAllocated) {
|
||||
std::array<uint8_t, sizeof(A)> buffer;
|
||||
A* a = new (buffer.data()) A(42);
|
||||
EXPECT_THAT(a->value(), 42);
|
||||
EXPECT_CALL(*destructorHook, Call(42));
|
||||
a->~A();
|
||||
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
|
||||
}
|
||||
|
||||
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
||||
constexpr size_t kCount = 5;
|
||||
std::array<uint8_t, sizeof(A) * kCount> buffer;
|
||||
A* as = new (buffer.data()) A[kCount];
|
||||
|
||||
std::vector<int> actual;
|
||||
for (A* a = as; a != as + kCount; ++a) {
|
||||
actual.push_back(a->value());
|
||||
}
|
||||
std::array<int, kCount> expected;
|
||||
for (int& element : expected) {
|
||||
element = -1;
|
||||
}
|
||||
EXPECT_THAT(actual, testing::ElementsAreArray(expected));
|
||||
|
||||
EXPECT_CALL(*destructorHook, Call(-1)).Times(kCount);
|
||||
for (A* a = as; a != as + kCount; ++a) {
|
||||
a->~A();
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "Cleaner.h"
|
||||
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@@ -14,6 +13,7 @@
|
||||
#include "Atomic.h"
|
||||
#include "TestSupport.hpp"
|
||||
#include "TestSupportCompilerGenerated.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using testing::_;
|
||||
|
||||
@@ -31,7 +31,7 @@ TEST(CleanerTest, ConcurrentCreation) {
|
||||
|
||||
int startedThreads = 0;
|
||||
bool allowRunning = false;
|
||||
std::vector<std::future<KInt>> futures;
|
||||
KStdVector<std::future<KInt>> futures;
|
||||
for (int i = 0; i < threadCount; ++i) {
|
||||
auto future = std::async(std::launch::async, [&startedThreads, &allowRunning]() {
|
||||
atomicAdd(&startedThreads, 1);
|
||||
@@ -44,7 +44,7 @@ TEST(CleanerTest, ConcurrentCreation) {
|
||||
while (atomicGet(&startedThreads) != threadCount) {
|
||||
}
|
||||
atomicSet(&allowRunning, true);
|
||||
std::vector<KInt> values;
|
||||
KStdVector<KInt> values;
|
||||
for (auto& future : futures) {
|
||||
values.push_back(future.get());
|
||||
}
|
||||
|
||||
@@ -6,45 +6,11 @@
|
||||
#ifndef RUNTIME_CPP_SUPPORT_H
|
||||
#define RUNTIME_CPP_SUPPORT_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
// A collection of backported utilities from future C++ versions.
|
||||
|
||||
namespace kotlin {
|
||||
namespace std_support {
|
||||
|
||||
////////////////////////// C++14 //////////////////////////
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using make_unsigned_t = typename std::make_unsigned<T>::type;
|
||||
|
||||
////////////////////////// C++17 //////////////////////////
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_trivially_destructible_v = std::is_trivially_destructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_nothrow_default_constructible_v = std::is_nothrow_default_constructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_copy_constructible_v = std::is_copy_constructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_copy_assignable_v = std::is_copy_assignable<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_move_constructible_v = std::is_move_constructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_move_assignable_v = std::is_move_assignable<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_nothrow_move_constructible_v = std::is_nothrow_move_constructible<T>::value;
|
||||
template <typename T>
|
||||
constexpr bool is_nothrow_move_assignable_v = std::is_nothrow_move_assignable<T>::value;
|
||||
|
||||
} // namespace std_support
|
||||
} // namespace kotlin
|
||||
|
||||
|
||||
@@ -107,11 +107,13 @@ _Unwind_Reason_Code unwindCallback(
|
||||
|
||||
THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false;
|
||||
|
||||
#if !OMIT_BACKTRACE && !USE_GCC_UNWIND
|
||||
SourceInfo getSourceInfo(KConstRef stackTrace, int index) {
|
||||
return disallowSourceInfo
|
||||
? SourceInfo { .fileName = nullptr, .lineNumber = -1, .column = -1 }
|
||||
: Kotlin_getSourceInfo(*PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace->array(), index));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -25,7 +25,14 @@
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message, ...);
|
||||
#if KONAN_ENABLE_ASSERT
|
||||
#define CURRENT_SOURCE_LOCATION __FILE__ ":" TOSTRING(__LINE__)
|
||||
#else
|
||||
// Do not generate location strings, when asserts are disabled to reduce code size.
|
||||
#define CURRENT_SOURCE_LOCATION nullptr
|
||||
#endif
|
||||
|
||||
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -35,7 +42,7 @@ inline RUNTIME_NORETURN void TODOImpl(const char* location) {
|
||||
|
||||
// TODO: Support format string when `RuntimeAssertFailed` supports it.
|
||||
inline RUNTIME_NORETURN void TODOImpl(const char* location, const char* message) {
|
||||
RuntimeAssertFailed(location, message);
|
||||
RuntimeAssertFailed(location, "%s", message);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
@@ -46,24 +53,31 @@ extern "C" const int KonanNeedDebugInfo;
|
||||
|
||||
#if KONAN_ENABLE_ASSERT
|
||||
// Use RuntimeAssert() in internal state checks, which could be ignored in production.
|
||||
#define RuntimeAssert(condition, format, ...) \
|
||||
if (KonanNeedDebugInfo && (!(condition))) { \
|
||||
RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), format, ##__VA_ARGS__); \
|
||||
}
|
||||
#define RuntimeAssert(condition, format, ...) \
|
||||
do { \
|
||||
if (KonanNeedDebugInfo && (!(condition))) { \
|
||||
RuntimeAssertFailed(CURRENT_SOURCE_LOCATION, format, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (false)
|
||||
#else
|
||||
#define RuntimeAssert(condition, message)
|
||||
#define RuntimeAssert(condition, format, ...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
// Use RuntimeCheck() in runtime checks that could fail due to external condition and shall lead
|
||||
// to program termination. Never compiled out.
|
||||
#define RuntimeCheck(condition, format, ...) \
|
||||
if (!(condition)) { \
|
||||
RuntimeAssertFailed(nullptr, format, ##__VA_ARGS__); \
|
||||
}
|
||||
// TODO: Consider using `CURRENT_SOURCE_LOCATION` when `KonanNeedDebugInfo` is `true`.
|
||||
#define RuntimeCheck(condition, format, ...) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
RuntimeAssertFailed(nullptr, format, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define TODO(...) \
|
||||
do { \
|
||||
::internal::TODOImpl(__FILE__ ":" TOSTRING(__LINE__), ##__VA_ARGS__); \
|
||||
::internal::TODOImpl(CURRENT_SOURCE_LOCATION, ##__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
#endif // RUNTIME_ASSERT_H
|
||||
|
||||
@@ -177,7 +177,7 @@ void BackRefFromAssociatedObject::releaseRef() {
|
||||
}
|
||||
|
||||
void BackRefFromAssociatedObject::detach() {
|
||||
RuntimeAssert(atomicGet(&refCount) == 0, "unexpected refCount")
|
||||
RuntimeAssert(atomicGet(&refCount) == 0, "unexpected refCount");
|
||||
obj_ = nullptr; // Handled in addRef/tryAddRef/releaseRef/ref.
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "Memory.h"
|
||||
|
||||
// TODO: Generalize for uses outside this file.
|
||||
@@ -38,8 +37,7 @@ class KRefSharedHolder {
|
||||
ForeignRefContext context_;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
kotlin::std_support::is_trivially_destructible_v<KRefSharedHolder>, "KRefSharedHolder destructor is not guaranteed to be called.");
|
||||
static_assert(std::is_trivially_destructible_v<KRefSharedHolder>, "KRefSharedHolder destructor is not guaranteed to be called.");
|
||||
|
||||
class BackRefFromAssociatedObject {
|
||||
public:
|
||||
@@ -68,7 +66,7 @@ class BackRefFromAssociatedObject {
|
||||
};
|
||||
|
||||
static_assert(
|
||||
kotlin::std_support::is_trivially_destructible_v<BackRefFromAssociatedObject>,
|
||||
std::is_trivially_destructible_v<BackRefFromAssociatedObject>,
|
||||
"BackRefFromAssociatedObject destructor is not guaranteed to be called.");
|
||||
|
||||
#endif // RUNTIME_MEMORYSHAREDREFS_HPP
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "Mutex.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
@@ -20,9 +21,9 @@ class MultiSourceQueue {
|
||||
public:
|
||||
class Producer;
|
||||
|
||||
// TODO: Consider switching from `std::list` to `SingleLockList` to hide the constructor
|
||||
// TODO: Consider switching from `KStdList` to `SingleLockList` to hide the constructor
|
||||
// and to not store the iterator.
|
||||
class Node : private Pinned {
|
||||
class Node : private Pinned, public KonanAllocatorAware {
|
||||
public:
|
||||
Node(const T& value, Producer* owner) noexcept : value_(value), owner_(owner) {}
|
||||
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
|
||||
T value_;
|
||||
std::atomic<Producer*> owner_; // `nullptr` signifies that `MultiSourceQueue` owns it.
|
||||
typename std::list<Node>::iterator position_;
|
||||
typename KStdList<Node>::iterator position_;
|
||||
};
|
||||
|
||||
class Producer {
|
||||
@@ -72,8 +73,8 @@ public:
|
||||
|
||||
private:
|
||||
MultiSourceQueue& owner_; // weak
|
||||
std::list<Node> queue_;
|
||||
std::list<Node*> deletionQueue_;
|
||||
KStdList<Node> queue_;
|
||||
KStdList<Node*> deletionQueue_;
|
||||
};
|
||||
|
||||
class Iterator {
|
||||
@@ -92,9 +93,9 @@ public:
|
||||
private:
|
||||
friend class MultiSourceQueue;
|
||||
|
||||
explicit Iterator(const typename std::list<Node>::iterator& position) noexcept : position_(position) {}
|
||||
explicit Iterator(const typename KStdList<Node>::iterator& position) noexcept : position_(position) {}
|
||||
|
||||
typename std::list<Node>::iterator position_;
|
||||
typename KStdList<Node>::iterator position_;
|
||||
};
|
||||
|
||||
class Iterable : MoveOnly {
|
||||
@@ -118,7 +119,7 @@ public:
|
||||
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
||||
void ApplyDeletions() noexcept {
|
||||
std::lock_guard<SpinLock> guard(mutex_);
|
||||
std::list<Node*> remainingDeletions;
|
||||
KStdList<Node*> remainingDeletions;
|
||||
|
||||
auto it = deletionQueue_.begin();
|
||||
while (it != deletionQueue_.end()) {
|
||||
@@ -139,10 +140,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Using `std::list` as it allows to implement `Collect` without memory allocations,
|
||||
// Using `KStdList` as it allows to implement `Collect` without memory allocations,
|
||||
// which is important for GC mark phase.
|
||||
std::list<Node> queue_;
|
||||
std::list<Node*> deletionQueue_;
|
||||
KStdList<Node> queue_;
|
||||
KStdList<Node*> deletionQueue_;
|
||||
SpinLock mutex_;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> Collect(MultiSourceQueue<T>& queue) {
|
||||
std::vector<T> result;
|
||||
KStdVector<T> Collect(MultiSourceQueue<T>& queue) {
|
||||
KStdVector<T> result;
|
||||
for (const auto& element : queue.Iter()) {
|
||||
result.push_back(element);
|
||||
}
|
||||
@@ -192,8 +193,8 @@ TEST(MultiSourceQueueTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<int> expected;
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<int> expected;
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
expected.push_back(i);
|
||||
@@ -223,8 +224,8 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
||||
constexpr int kStartCount = 50;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
std::vector<int> expectedBefore;
|
||||
std::vector<int> expectedAfter;
|
||||
KStdVector<int> expectedBefore;
|
||||
KStdVector<int> expectedAfter;
|
||||
IntQueue::Producer producer(queue);
|
||||
for (int i = 0; i < kStartCount; ++i) {
|
||||
expectedBefore.push_back(i);
|
||||
@@ -236,7 +237,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -251,7 +252,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<int> actualBefore;
|
||||
KStdVector<int> actualBefore;
|
||||
{
|
||||
auto iter = queue.Iter();
|
||||
while (readyCount < kThreadCount) {
|
||||
@@ -282,7 +283,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([&queue, i, &canStart, &readyCount, &startedCount]() {
|
||||
IntQueue::Producer producer(queue);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "KAssert.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Memory.h"
|
||||
@@ -51,7 +50,7 @@ OBJ_GETTER0(Kotlin_native_internal_undefined) {
|
||||
}
|
||||
|
||||
void* Kotlin_interop_malloc(KLong size, KInt align) {
|
||||
if (size < 0 || static_cast<kotlin::std_support::make_unsigned_t<decltype(size)>>(size) > std::numeric_limits<size_t>::max()) {
|
||||
if (size < 0 || static_cast<std::make_unsigned_t<decltype(size)>>(size) > std::numeric_limits<size_t>::max()) {
|
||||
return nullptr;
|
||||
}
|
||||
RuntimeAssert(align > 0, "Unsupported alignment");
|
||||
|
||||
@@ -124,7 +124,6 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
|
||||
}
|
||||
|
||||
static Class getOrCreateClass(const TypeInfo* typeInfo);
|
||||
static void initializeClass(Class clazz);
|
||||
|
||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||
|
||||
@@ -768,16 +767,6 @@ static KStdVector<const TypeInfo*> getProtocolsAsInterfaces(Class clazz) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static const TypeInfo* getMostSpecificKotlinClass(const TypeInfo* typeInfo) {
|
||||
const TypeInfo* result = typeInfo;
|
||||
while (getTypeAdapter(result) == nullptr) {
|
||||
result = result->superType_;
|
||||
RuntimeAssert(result != nullptr, "");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int getVtableSize(const TypeInfo* typeInfo) {
|
||||
for (const TypeInfo* current = typeInfo; current != nullptr; current = current->superType_) {
|
||||
auto typeAdapter = getTypeAdapter(current);
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace konan {
|
||||
|
||||
// Console operations.
|
||||
void consoleInit();
|
||||
void consolePrintf(const char* format, ...);
|
||||
void consoleErrorf(const char* format, ...);
|
||||
void consolePrintf(const char* format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void consoleErrorf(const char* format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes);
|
||||
void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes);
|
||||
// Negative return value denotes that read wasn't successful.
|
||||
@@ -46,8 +46,8 @@ void onThreadExit(void (*destructor)(void*), void* destructorParameter);
|
||||
// memcpy/memmove/memcmp are not here intentionally, as frequently implemented/optimized
|
||||
// by C compiler.
|
||||
void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLen);
|
||||
int snprintf(char* buffer, size_t size, const char* format, ...);
|
||||
int vsnprintf(char* buffer, size_t size, const char* format, va_list args);
|
||||
int snprintf(char* buffer, size_t size, const char* format, ...) __attribute__((format(printf, 3, 4)));
|
||||
int vsnprintf(char* buffer, size_t size, const char* format, va_list args) __attribute__((format(printf, 3, 0)));
|
||||
size_t strnlen(const char* buffer, size_t maxSize);
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "Alloc.h"
|
||||
#include "Mutex.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
@@ -20,29 +21,43 @@ namespace kotlin {
|
||||
template <typename Value, typename Mutex = SpinLock>
|
||||
class SingleLockList : private Pinned {
|
||||
public:
|
||||
class Node : Pinned {
|
||||
class Node;
|
||||
|
||||
private:
|
||||
class NodeDeleter {
|
||||
public:
|
||||
Value* Get() noexcept { return &value; }
|
||||
void operator()(Node* node) const { delete node; }
|
||||
};
|
||||
|
||||
using NodeOwner = std::unique_ptr<Node, NodeDeleter>;
|
||||
|
||||
public:
|
||||
class Node : private Pinned, public KonanAllocatorAware {
|
||||
public:
|
||||
Value* Get() noexcept { return &value_; }
|
||||
|
||||
private:
|
||||
friend class SingleLockList;
|
||||
|
||||
template <typename... Args>
|
||||
Node(Args... args) noexcept : value(args...) {}
|
||||
Node(Args&&... args) noexcept : value_(std::forward<Args>(args)...) {}
|
||||
|
||||
Value value;
|
||||
std::unique_ptr<Node> next;
|
||||
Node* previous = nullptr; // weak
|
||||
// Make sure `Node` can only be deleted by `SingleLockList` itself.
|
||||
~Node() = default;
|
||||
|
||||
Value value_;
|
||||
NodeOwner next_;
|
||||
Node* previous_ = nullptr; // weak
|
||||
};
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
explicit Iterator(Node* node) noexcept : node_(node) {}
|
||||
|
||||
Value& operator*() noexcept { return node_->value; }
|
||||
Value& operator*() noexcept { return node_->value_; }
|
||||
|
||||
Iterator& operator++() noexcept {
|
||||
node_ = node_->next.get();
|
||||
node_ = node_->next_.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -67,36 +82,56 @@ public:
|
||||
std::unique_lock<Mutex> guard_;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
Node* Emplace(Args... args) noexcept {
|
||||
auto* nodePtr = new Node(args...);
|
||||
std::unique_ptr<Node> node(nodePtr);
|
||||
std::lock_guard<Mutex> guard(mutex_);
|
||||
if (root_) {
|
||||
root_->previous = node.get();
|
||||
~SingleLockList() {
|
||||
AssertCorrectUnsafe();
|
||||
// Make sure not to blow up the stack by nested `~Node` calls.
|
||||
for (auto node = std::move(root_); node != nullptr; node = std::move(node->next_)) {
|
||||
}
|
||||
node->next = std::move(root_);
|
||||
last_ = nullptr;
|
||||
AssertCorrectUnsafe();
|
||||
}
|
||||
|
||||
// TODO: Consider making `Emplace` append to `last_`.
|
||||
template <typename... Args>
|
||||
Node* Emplace(Args&&... args) noexcept {
|
||||
auto* nodePtr = new Node(std::forward<Args>(args)...);
|
||||
NodeOwner node(nodePtr);
|
||||
std::lock_guard<Mutex> guard(mutex_);
|
||||
AssertCorrectUnsafe();
|
||||
if (root_) {
|
||||
root_->previous_ = node.get();
|
||||
} else {
|
||||
last_ = nodePtr;
|
||||
}
|
||||
node->next_ = std::move(root_);
|
||||
root_ = std::move(node);
|
||||
AssertCorrectUnsafe();
|
||||
return nodePtr;
|
||||
}
|
||||
|
||||
// Using `node` including its referred `Value` after `Erase` is undefined behaviour.
|
||||
void Erase(Node* node) noexcept {
|
||||
std::lock_guard<Mutex> guard(mutex_);
|
||||
AssertCorrectUnsafe();
|
||||
if (last_ == node) {
|
||||
last_ = node->previous_;
|
||||
}
|
||||
if (root_.get() == node) {
|
||||
root_ = std::move(node->next);
|
||||
root_ = std::move(node->next_);
|
||||
if (root_) {
|
||||
root_->previous = nullptr;
|
||||
root_->previous_ = nullptr;
|
||||
}
|
||||
AssertCorrectUnsafe();
|
||||
return;
|
||||
}
|
||||
auto* previous = node->previous;
|
||||
auto* previous = node->previous_;
|
||||
RuntimeAssert(previous != nullptr, "Only the root node doesn't have the previous node");
|
||||
auto ownedNode = std::move(previous->next);
|
||||
previous->next = std::move(node->next);
|
||||
if (auto& next = previous->next) {
|
||||
next->previous = previous;
|
||||
auto ownedNode = std::move(previous->next_);
|
||||
previous->next_ = std::move(node->next_);
|
||||
if (auto& next = previous->next_) {
|
||||
next->previous_ = previous;
|
||||
}
|
||||
AssertCorrectUnsafe();
|
||||
}
|
||||
|
||||
// Returned value locks `this` to perform safe iteration. `this` unlocks when
|
||||
@@ -109,7 +144,19 @@ public:
|
||||
Iterable Iter() noexcept { return Iterable(this); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Node> root_;
|
||||
// Expects `mutex_` to be held by the current thread.
|
||||
ALWAYS_INLINE void AssertCorrectUnsafe() const noexcept {
|
||||
if (root_ == nullptr) {
|
||||
RuntimeAssert(last_ == nullptr, "last_ must be null");
|
||||
} else {
|
||||
RuntimeAssert(root_->previous_ == nullptr, "root_ must not have previous_");
|
||||
RuntimeAssert(last_ != nullptr, "last_ must not be null");
|
||||
RuntimeAssert(last_->next_ == nullptr, "last_ must not have next_");
|
||||
}
|
||||
}
|
||||
|
||||
NodeOwner root_;
|
||||
Node* last_ = nullptr;
|
||||
Mutex mutex_;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
#include "SingleLockList.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
@@ -47,7 +48,7 @@ TEST(SingleLockListTest, EmplaceAndIter) {
|
||||
list.Emplace(kSecond);
|
||||
list.Emplace(kThird);
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -65,7 +66,7 @@ TEST(SingleLockListTest, EmplaceEraseAndIter) {
|
||||
list.Emplace(kThird);
|
||||
list.Erase(secondNode);
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -76,7 +77,7 @@ TEST(SingleLockListTest, EmplaceEraseAndIter) {
|
||||
TEST(SingleLockListTest, IterEmpty) {
|
||||
IntList list;
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -97,7 +98,7 @@ TEST(SingleLockListTest, EraseToEmptyEmplaceAndIter) {
|
||||
list.Emplace(kThird);
|
||||
list.Emplace(kFourth);
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -110,8 +111,8 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<int> expected;
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<int> expected;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
expected.push_back(i);
|
||||
threads.emplace_back([i, &list, &canStart, &readyCount]() {
|
||||
@@ -129,7 +130,7 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -140,14 +141,14 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
||||
TEST(SingleLockListTest, ConcurrentErase) {
|
||||
IntList list;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::vector<IntList::Node*> items;
|
||||
KStdVector<IntList::Node*> items;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
items.push_back(list.Emplace(i));
|
||||
}
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (auto* item : items) {
|
||||
threads.emplace_back([item, &list, &canStart, &readyCount]() {
|
||||
++readyCount;
|
||||
@@ -164,7 +165,7 @@ TEST(SingleLockListTest, ConcurrentErase) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::vector<int> actual;
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.Iter()) {
|
||||
actual.push_back(element);
|
||||
}
|
||||
@@ -177,8 +178,8 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
constexpr int kStartCount = 50;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
std::deque<int> expectedBefore;
|
||||
std::vector<int> expectedAfter;
|
||||
KStdDeque<int> expectedBefore;
|
||||
KStdVector<int> expectedAfter;
|
||||
for (int i = 0; i < kStartCount; ++i) {
|
||||
expectedBefore.push_front(i);
|
||||
expectedAfter.push_back(i);
|
||||
@@ -187,7 +188,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -199,7 +200,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<int> actualBefore;
|
||||
KStdVector<int> actualBefore;
|
||||
{
|
||||
auto iter = list.Iter();
|
||||
canStart = true;
|
||||
@@ -217,7 +218,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
std::vector<int> actualAfter;
|
||||
KStdVector<int> actualAfter;
|
||||
for (int element : list.Iter()) {
|
||||
actualAfter.push_back(element);
|
||||
}
|
||||
@@ -229,8 +230,8 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
IntList list;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
std::deque<int> expectedBefore;
|
||||
std::vector<IntList::Node*> items;
|
||||
KStdDeque<int> expectedBefore;
|
||||
KStdVector<IntList::Node*> items;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
expectedBefore.push_front(i);
|
||||
items.push_back(list.Emplace(i));
|
||||
@@ -238,7 +239,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (auto* item : items) {
|
||||
threads.emplace_back([item, &list, &canStart, &startedCount]() {
|
||||
while (!canStart) {
|
||||
@@ -248,7 +249,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<int> actualBefore;
|
||||
KStdVector<int> actualBefore;
|
||||
{
|
||||
auto iter = list.Iter();
|
||||
canStart = true;
|
||||
@@ -266,7 +267,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
std::vector<int> actualAfter;
|
||||
KStdVector<int> actualAfter;
|
||||
for (int element : list.Iter()) {
|
||||
actualAfter.push_back(element);
|
||||
}
|
||||
@@ -298,10 +299,48 @@ TEST(SingleLockListTest, PinnedType) {
|
||||
|
||||
list.Erase(itemNode);
|
||||
|
||||
std::vector<PinnedType*> actualAfter;
|
||||
KStdVector<PinnedType*> actualAfter;
|
||||
for (auto& element : list.Iter()) {
|
||||
actualAfter.push_back(&element);
|
||||
}
|
||||
|
||||
EXPECT_THAT(actualAfter, testing::IsEmpty());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class WithDestructorHook;
|
||||
|
||||
using DestructorHook = void(WithDestructorHook*);
|
||||
|
||||
class WithDestructorHook : private Pinned {
|
||||
public:
|
||||
explicit WithDestructorHook(std::function<DestructorHook> hook) : hook_(std::move(hook)) {}
|
||||
|
||||
~WithDestructorHook() { hook_(this); }
|
||||
|
||||
private:
|
||||
std::function<DestructorHook> hook_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SingleLockListTest, Destructor) {
|
||||
testing::StrictMock<testing::MockFunction<DestructorHook>> hook;
|
||||
{
|
||||
SingleLockList<WithDestructorHook> list;
|
||||
auto* first = list.Emplace(hook.AsStdFunction())->Get();
|
||||
auto* second = list.Emplace(hook.AsStdFunction())->Get();
|
||||
auto* third = list.Emplace(hook.AsStdFunction())->Get();
|
||||
{
|
||||
testing::InSequence seq;
|
||||
// `list` is `third`->`second`->`first`. If destruction
|
||||
// were to cause recursion, the order of destructors
|
||||
// would've been backwards.
|
||||
EXPECT_CALL(hook, Call(third));
|
||||
EXPECT_CALL(hook, Call(second));
|
||||
EXPECT_CALL(hook, Call(first));
|
||||
}
|
||||
}
|
||||
testing::Mock::VerifyAndClear(&hook);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ RUNTIME_USED RUNTIME_WEAK extern "C" char* Konan_cxa_demangle(
|
||||
|
||||
namespace std {
|
||||
RUNTIME_WEAK void __throw_length_error(const char* __s __attribute__((unused))) {
|
||||
RuntimeAssert(false, __s);
|
||||
RuntimeCheck(false, "%s", __s);
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
@@ -21,7 +20,7 @@ public:
|
||||
explicit ScopedStrictMockFunction(Mock** globalMockLocation) : globalMockLocation_(globalMockLocation) {
|
||||
RuntimeCheck(globalMockLocation != nullptr, "ScopedStrictMockFunction needs non-null global mock location");
|
||||
RuntimeCheck(*globalMockLocation == nullptr, "ScopedStrictMockFunction needs null global mock");
|
||||
mock_ = kotlin::std_support::make_unique<Mock>();
|
||||
mock_ = make_unique<Mock>();
|
||||
*globalMockLocation_ = mock_.get();
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ public:
|
||||
private:
|
||||
// Can be null if moved-out of.
|
||||
Mock** globalMockLocation_;
|
||||
std::unique_ptr<Mock> mock_;
|
||||
KStdUniquePtr<Mock> mock_;
|
||||
};
|
||||
|
||||
ScopedStrictMockFunction<KInt()> ScopedCreateCleanerWorkerMock();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
@@ -60,6 +61,8 @@ typedef ObjHeader* KRef;
|
||||
typedef const ObjHeader* KConstRef;
|
||||
typedef const ArrayHeader* KString;
|
||||
|
||||
// TODO: Consider moving these into `kotlin::std_support` namespace keeping STL names.
|
||||
|
||||
// Definitions of STL classes used inside Konan runtime.
|
||||
typedef std::basic_string<char, std::char_traits<char>,
|
||||
KonanAllocator<char>> KStdString;
|
||||
@@ -81,6 +84,13 @@ template<class Value>
|
||||
using KStdVector = std::vector<Value, KonanAllocator<Value>>;
|
||||
template<class Value>
|
||||
using KStdList = std::list<Value, KonanAllocator<Value>>;
|
||||
template <class Value>
|
||||
using KStdUniquePtr = std::unique_ptr<Value, KonanDeleter<Value>>;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
KStdUniquePtr<T> make_unique(Args&&... args) noexcept {
|
||||
return KStdUniquePtr<T>(konanConstructInstance<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#ifndef RUNTIME_UTILS_H
|
||||
#define RUNTIME_UTILS_H
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
// A helper for implementing classes with disabled copy constructor and copy assignment.
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <type_traits>
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
@@ -30,21 +30,21 @@ public:
|
||||
} // namespace
|
||||
|
||||
TEST(UtilsTest, MoveOnlyImpl) {
|
||||
static_assert(std_support::is_nothrow_default_constructible_v<MoveOnlyImpl>, "Must be nothrow default constructible");
|
||||
static_assert(std_support::is_nothrow_destructible_v<MoveOnlyImpl>, "Must be nothrow destructible");
|
||||
static_assert(!std_support::is_copy_constructible_v<MoveOnlyImpl>, "Must not be copy constructible");
|
||||
static_assert(!std_support::is_copy_assignable_v<MoveOnlyImpl>, "Must not be copy assignable");
|
||||
static_assert(std_support::is_nothrow_move_constructible_v<MoveOnlyImpl>, "Must be nothrow move constructible");
|
||||
static_assert(std_support::is_nothrow_move_assignable_v<MoveOnlyImpl>, "Must be nothrow move assignable");
|
||||
static_assert(std::is_nothrow_default_constructible_v<MoveOnlyImpl>, "Must be nothrow default constructible");
|
||||
static_assert(std::is_nothrow_destructible_v<MoveOnlyImpl>, "Must be nothrow destructible");
|
||||
static_assert(!std::is_copy_constructible_v<MoveOnlyImpl>, "Must not be copy constructible");
|
||||
static_assert(!std::is_copy_assignable_v<MoveOnlyImpl>, "Must not be copy assignable");
|
||||
static_assert(std::is_nothrow_move_constructible_v<MoveOnlyImpl>, "Must be nothrow move constructible");
|
||||
static_assert(std::is_nothrow_move_assignable_v<MoveOnlyImpl>, "Must be nothrow move assignable");
|
||||
static_assert(sizeof(MoveOnlyImpl) == sizeof(A), "Must not increase size");
|
||||
}
|
||||
|
||||
TEST(UtilsTest, PinnedImpl) {
|
||||
static_assert(std_support::is_nothrow_default_constructible_v<PinnedImpl>, "Must be nothrow default constructible");
|
||||
static_assert(std_support::is_nothrow_destructible_v<PinnedImpl>, "Must be nothrow destructible");
|
||||
static_assert(!std_support::is_copy_constructible_v<PinnedImpl>, "Must not be copy constructible");
|
||||
static_assert(!std_support::is_copy_assignable_v<PinnedImpl>, "Must not be copy assignable");
|
||||
static_assert(!std_support::is_move_constructible_v<PinnedImpl>, "Must not be move constructible");
|
||||
static_assert(!std_support::is_move_assignable_v<PinnedImpl>, "Must not be move assignable");
|
||||
static_assert(std::is_nothrow_default_constructible_v<PinnedImpl>, "Must be nothrow default constructible");
|
||||
static_assert(std::is_nothrow_destructible_v<PinnedImpl>, "Must be nothrow destructible");
|
||||
static_assert(!std::is_copy_constructible_v<PinnedImpl>, "Must not be copy constructible");
|
||||
static_assert(!std::is_copy_assignable_v<PinnedImpl>, "Must not be copy assignable");
|
||||
static_assert(!std::is_move_constructible_v<PinnedImpl>, "Must not be move constructible");
|
||||
static_assert(!std::is_move_assignable_v<PinnedImpl>, "Must not be move assignable");
|
||||
static_assert(sizeof(PinnedImpl) == sizeof(A), "Must not increase size");
|
||||
}
|
||||
|
||||
@@ -476,7 +476,7 @@ class State {
|
||||
|
||||
template <typename F>
|
||||
void waitNativeWorkersTerminationUnlocked(bool checkLeaks, F waitForWorker) {
|
||||
std::vector<std::pair<KInt, pthread_t>> workersToWait;
|
||||
KStdVector<std::pair<KInt, pthread_t>> workersToWait;
|
||||
{
|
||||
Locker locker(&lock_);
|
||||
|
||||
@@ -511,7 +511,7 @@ class State {
|
||||
|
||||
if (remainingNativeWorkers != 0) {
|
||||
konan::consoleErrorf(
|
||||
"Unfinished workers detected, %lu workers leaked!\n"
|
||||
"Unfinished workers detected, %zu workers leaked!\n"
|
||||
"Use `Platform.isMemoryLeakCheckerActive = false` to avoid this check.\n",
|
||||
remainingNativeWorkers);
|
||||
konan::consoleFlush();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Alloc.h"
|
||||
#include "Memory.h"
|
||||
#include "TypeInfo.h"
|
||||
#include "Utils.hpp"
|
||||
@@ -17,7 +18,7 @@ namespace kotlin {
|
||||
namespace mm {
|
||||
|
||||
// Optional data that's lazily allocated only for objects that need it.
|
||||
class ExtraObjectData : private Pinned {
|
||||
class ExtraObjectData : private Pinned, public KonanAllocatorAware {
|
||||
public:
|
||||
MetaObjHeader* AsMetaObjHeader() noexcept { return reinterpret_cast<MetaObjHeader*>(this); }
|
||||
static ExtraObjectData& FromMetaObjHeader(MetaObjHeader* header) noexcept { return *reinterpret_cast<ExtraObjectData*>(header); }
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
void ProcessThread(mm::ThreadData* threadData) noexcept;
|
||||
|
||||
// Lock registry for safe iteration.
|
||||
// TODO: Iteration over `globals_` will be slow, because it's `std::list` collected at different times from
|
||||
// TODO: Iteration over `globals_` will be slow, because it's `KStdList` collected at different times from
|
||||
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
||||
// much of a problem is it.
|
||||
Iterable Iter() noexcept { return globals_.Iter(); }
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Alignment.hpp"
|
||||
#include "Alloc.h"
|
||||
#include "CppSupport.hpp"
|
||||
#include "Memory.h"
|
||||
#include "Mutex.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
@@ -31,15 +32,14 @@ class ObjectFactoryStorage : private Pinned {
|
||||
static_assert(IsValidAlignment(DataAlignment), "DataAlignment is not a valid alignment");
|
||||
|
||||
public:
|
||||
// This class does not know its size at compile-time.
|
||||
// This class does not know its size at compile-time. Does not inherit from `KonanAllocatorAware` because
|
||||
// in `KonanAllocatorAware::operator new(size_t size, KonanAllocTag)` `size` would be incorrect.
|
||||
class Node : private Pinned {
|
||||
constexpr static size_t DataOffset() noexcept { return AlignUp(sizeof(Node), DataAlignment); }
|
||||
|
||||
public:
|
||||
~Node() = default;
|
||||
|
||||
static void operator delete(void* ptr) noexcept { konanFreeMemory(ptr); }
|
||||
|
||||
// Note: This can only be trivially destructible data, as nobody can invoke its destructor.
|
||||
void* Data() noexcept {
|
||||
constexpr size_t kDataOffset = DataOffset();
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
Node() noexcept = default;
|
||||
|
||||
static void* operator new(size_t size, size_t dataSize) noexcept {
|
||||
static KStdUniquePtr<Node> Create(size_t dataSize) noexcept {
|
||||
size_t dataSizeAligned = AlignUp(dataSize, DataAlignment);
|
||||
size_t totalAlignment = std::max(alignof(Node), DataAlignment);
|
||||
size_t totalSize = AlignUp(sizeof(Node) + dataSizeAligned, totalAlignment);
|
||||
@@ -73,10 +73,10 @@ public:
|
||||
konan::abort();
|
||||
}
|
||||
RuntimeAssert(IsAligned(ptr, totalAlignment), "Allocator returned unaligned to %zu pointer %p", totalAlignment, ptr);
|
||||
return ptr;
|
||||
return KStdUniquePtr<Node>(new (ptr) Node());
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> next_;
|
||||
KStdUniquePtr<Node> next_;
|
||||
// There's some more data of an unknown (at compile-time) size here, but it cannot be represented
|
||||
// with C++ members.
|
||||
};
|
||||
@@ -89,13 +89,11 @@ public:
|
||||
|
||||
Node& Insert(size_t dataSize) noexcept {
|
||||
AssertCorrect();
|
||||
auto* nodePtr = new (dataSize) Node();
|
||||
std::unique_ptr<Node> node(nodePtr);
|
||||
auto node = Node::Create(dataSize);
|
||||
auto* nodePtr = node.get();
|
||||
if (!root_) {
|
||||
RuntimeAssert(last_ == nullptr, "Unsynchronized root_ and last_");
|
||||
root_ = std::move(node);
|
||||
} else {
|
||||
RuntimeAssert(last_ != nullptr, "Unsynchronized root_ and last_");
|
||||
last_->next_ = std::move(node);
|
||||
}
|
||||
|
||||
@@ -108,7 +106,7 @@ public:
|
||||
template <typename T, typename... Args>
|
||||
Node& Insert(Args&&... args) noexcept {
|
||||
static_assert(alignof(T) <= DataAlignment, "Cannot insert type with alignment bigger than DataAlignment");
|
||||
static_assert(std_support::is_trivially_destructible_v<T>, "Type must be trivially destructible");
|
||||
static_assert(std::is_trivially_destructible_v<T>, "Type must be trivially destructible");
|
||||
auto& node = Insert(sizeof(T));
|
||||
new (node.Data()) T(std::forward<Args>(args)...);
|
||||
return node;
|
||||
@@ -155,7 +153,7 @@ public:
|
||||
}
|
||||
|
||||
ObjectFactoryStorage& owner_; // weak
|
||||
std::unique_ptr<Node> root_;
|
||||
KStdUniquePtr<Node> root_;
|
||||
Node* last_ = nullptr;
|
||||
};
|
||||
|
||||
@@ -197,6 +195,11 @@ public:
|
||||
std::unique_lock<SpinLock> guard_;
|
||||
};
|
||||
|
||||
~ObjectFactoryStorage() {
|
||||
// Make sure not to blow up the stack by nested `~Node` calls.
|
||||
for (auto node = std::move(root_); node != nullptr; node = std::move(node->next_)) {}
|
||||
}
|
||||
|
||||
// Lock `ObjectFactoryStorage` for safe iteration.
|
||||
Iterable Iter() noexcept { return Iterable(*this); }
|
||||
|
||||
@@ -236,7 +239,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> root_;
|
||||
KStdUniquePtr<Node> root_;
|
||||
Node* last_ = nullptr;
|
||||
SpinLock mutex_;
|
||||
};
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "CppSupport.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
@@ -24,8 +25,8 @@ using ObjectFactoryStorageRegular = ObjectFactoryStorage<alignof(void*)>;
|
||||
namespace {
|
||||
|
||||
template <size_t DataAlignment>
|
||||
std::vector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||
std::vector<void*> result;
|
||||
KStdVector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||
KStdVector<void*> result;
|
||||
for (auto& node : storage.Iter()) {
|
||||
result.push_back(node.Data());
|
||||
}
|
||||
@@ -33,8 +34,8 @@ std::vector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||
}
|
||||
|
||||
template <typename T, size_t DataAlignment>
|
||||
std::vector<T> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||
std::vector<T> result;
|
||||
KStdVector<T> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||
KStdVector<T> result;
|
||||
for (auto& node : storage.Iter()) {
|
||||
result.push_back(*static_cast<T*>(node.Data()));
|
||||
}
|
||||
@@ -300,8 +301,8 @@ TEST(ObjectFactoryStorageTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<int> expected;
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<int> expected;
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
expected.push_back(i);
|
||||
@@ -332,8 +333,8 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
||||
constexpr int kStartCount = 50;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
std::vector<int> expectedBefore;
|
||||
std::vector<int> expectedAfter;
|
||||
KStdVector<int> expectedBefore;
|
||||
KStdVector<int> expectedAfter;
|
||||
ObjectFactoryStorageRegular::Producer producer(storage);
|
||||
for (int i = 0; i < kStartCount; ++i) {
|
||||
expectedBefore.push_back(i);
|
||||
@@ -345,7 +346,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -360,7 +361,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<int> actualBefore;
|
||||
KStdVector<int> actualBefore;
|
||||
{
|
||||
auto iter = storage.Iter();
|
||||
while (readyCount < kThreadCount) {
|
||||
@@ -391,7 +392,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
|
||||
constexpr int kStartCount = 50;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
std::vector<int> expectedAfter;
|
||||
KStdVector<int> expectedAfter;
|
||||
ObjectFactoryStorageRegular::Producer producer(storage);
|
||||
for (int i = 0; i < kStartCount; ++i) {
|
||||
if (i % 2 == 0) {
|
||||
@@ -404,7 +405,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -449,15 +450,15 @@ using mm::ObjectFactory;
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<TypeInfo> MakeObjectTypeInfo(int32_t size) {
|
||||
auto typeInfo = std_support::make_unique<TypeInfo>();
|
||||
KStdUniquePtr<TypeInfo> MakeObjectTypeInfo(int32_t size) {
|
||||
auto typeInfo = make_unique<TypeInfo>();
|
||||
typeInfo->typeInfo_ = typeInfo.get();
|
||||
typeInfo->instanceSize_ = size;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
std::unique_ptr<TypeInfo> MakeArrayTypeInfo(int32_t elementSize) {
|
||||
auto typeInfo = std_support::make_unique<TypeInfo>();
|
||||
KStdUniquePtr<TypeInfo> MakeArrayTypeInfo(int32_t elementSize) {
|
||||
auto typeInfo = make_unique<TypeInfo>();
|
||||
typeInfo->typeInfo_ = typeInfo.get();
|
||||
typeInfo->instanceSize_ = -elementSize;
|
||||
return typeInfo;
|
||||
@@ -537,9 +538,9 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
KStdVector<std::thread> threads;
|
||||
std::mutex expectedMutex;
|
||||
std::vector<ObjHeader*> expected;
|
||||
KStdVector<ObjHeader*> expected;
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([&typeInfo, &objectFactory, &canStart, &readyCount, &expected, &expectedMutex]() {
|
||||
@@ -564,7 +565,7 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
|
||||
}
|
||||
|
||||
auto iter = objectFactory.Iter();
|
||||
std::vector<ObjHeader*> actual;
|
||||
KStdVector<ObjHeader*> actual;
|
||||
for (auto it = iter.begin(); it != iter.end(); ++it) {
|
||||
actual.push_back(it.GetObjHeader());
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
void ProcessDeletions() noexcept;
|
||||
|
||||
// Lock registry for safe iteration.
|
||||
// TODO: Iteration over `stableRefs_` will be slow, because it's `std::list` collected at different times from
|
||||
// TODO: Iteration over `stableRefs_` will be slow, because it's `KStdList` collected at different times from
|
||||
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
||||
// much of a problem is it.
|
||||
Iterable Iter() noexcept { return stableRefs_.Iter(); }
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
@@ -22,7 +23,7 @@ public:
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
explicit Iterator(std::vector<ObjHeader*>::iterator iterator) : iterator_(iterator) {}
|
||||
explicit Iterator(KStdVector<ObjHeader*>::iterator iterator) : iterator_(iterator) {}
|
||||
|
||||
ObjHeader** operator*() noexcept { return &*iterator_; }
|
||||
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
bool operator!=(const Iterator& rhs) const noexcept { return iterator_ != rhs.iterator_; }
|
||||
|
||||
private:
|
||||
std::vector<ObjHeader*>::iterator iterator_;
|
||||
KStdVector<ObjHeader*>::iterator iterator_;
|
||||
};
|
||||
|
||||
// Add TLS record. Can only be called before `Commit`.
|
||||
@@ -64,9 +65,9 @@ private:
|
||||
|
||||
ObjHeader** Lookup(Entry entry, int index) noexcept;
|
||||
|
||||
std::vector<ObjHeader*> storage_;
|
||||
// TODO: `std::unordered_map` is probably the wrong container here.
|
||||
std::unordered_map<Key, Entry> map_;
|
||||
KStdVector<ObjHeader*> storage_;
|
||||
// TODO: `KStdUnorderedMap` is probably the wrong container here.
|
||||
KStdUnorderedMap<Key, Entry> map_;
|
||||
State state_ = State::kBuilding;
|
||||
int size_ = 0; // Only used in `State::kBuilding`
|
||||
std::pair<Key, Entry> lastKeyAndEntry_;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
@@ -54,12 +56,12 @@ TEST(ThreadLocalStorageTest, Iterate) {
|
||||
tls.AddRecord(&key2, 2);
|
||||
tls.Commit();
|
||||
|
||||
std::vector<ObjHeader**> expected;
|
||||
KStdVector<ObjHeader**> expected;
|
||||
expected.push_back(tls.Lookup(&key1, 0));
|
||||
expected.push_back(tls.Lookup(&key2, 0));
|
||||
expected.push_back(tls.Lookup(&key2, 1));
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
@@ -78,12 +80,12 @@ TEST(ThreadLocalStorageTest, AddRecordEmpty) {
|
||||
tls.AddRecord(&key3, 2);
|
||||
tls.Commit();
|
||||
|
||||
std::vector<ObjHeader**> expected;
|
||||
KStdVector<ObjHeader**> expected;
|
||||
expected.push_back(tls.Lookup(&key1, 0));
|
||||
expected.push_back(tls.Lookup(&key3, 0));
|
||||
expected.push_back(tls.Lookup(&key3, 1));
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
@@ -99,10 +101,10 @@ TEST(ThreadLocalStorageTest, AddRecordSameSize) {
|
||||
tls.AddRecord(&key1, 1);
|
||||
tls.Commit();
|
||||
|
||||
std::vector<ObjHeader**> expected;
|
||||
KStdVector<ObjHeader**> expected;
|
||||
expected.push_back(tls.Lookup(&key1, 0));
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
@@ -115,7 +117,7 @@ TEST(ThreadLocalStorageTest, NoRecords) {
|
||||
|
||||
tls.Commit();
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
@@ -130,7 +132,7 @@ TEST(ThreadLocalStorageTest, ClearEmpty) {
|
||||
|
||||
tls.Clear();
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
@@ -147,7 +149,7 @@ TEST(ThreadLocalStorageTest, ClearNonEmpty) {
|
||||
|
||||
tls.Clear();
|
||||
|
||||
std::vector<ObjHeader**> actual;
|
||||
KStdVector<ObjHeader**> actual;
|
||||
for (auto item : tls) {
|
||||
actual.push_back(item);
|
||||
}
|
||||
|
||||
@@ -232,10 +232,13 @@ OBJ_GETTER(Kotlin_boxDouble, KDouble value);
|
||||
@end;
|
||||
|
||||
static void injectToRuntime() {
|
||||
RuntimeCheck(Kotlin_ObjCExport_toKotlinSelector == nullptr, "runtime injected twice");
|
||||
// If the code below fails, then it is most likely caused by KT-42254.
|
||||
constexpr const char* errorMessage = "runtime injected twice; https://youtrack.jetbrains.com/issue/KT-42254 might be related";
|
||||
|
||||
RuntimeCheck(Kotlin_ObjCExport_toKotlinSelector == nullptr, errorMessage);
|
||||
Kotlin_ObjCExport_toKotlinSelector = @selector(toKotlin:);
|
||||
|
||||
RuntimeCheck(Kotlin_ObjCExport_releaseAsAssociatedObjectSelector == nullptr, "runtime injected twice");
|
||||
RuntimeCheck(Kotlin_ObjCExport_releaseAsAssociatedObjectSelector == nullptr, errorMessage);
|
||||
Kotlin_ObjCExport_releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject);
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -15,6 +15,25 @@ private fun mainImpl(args: Array<String>, konancMain: (Array<String>) -> Unit) {
|
||||
when (utilityName) {
|
||||
"konanc" ->
|
||||
konancMain(utilityArgs)
|
||||
"kotlinc" -> {
|
||||
println("""
|
||||
NOTE: you are running "kotlinc" CLI tool from Kotlin/Native distribution,
|
||||
it runs Kotlin/Native compiler that produces native binaries from Kotlin code.
|
||||
If your intention was to compile Kotlin code to JVM bytecode instead, then you
|
||||
need to use "kotlinc" from the main Kotlin distribution (e.g. it can be
|
||||
downloaded as kotlin-compiler-X.Y.ZZ.zip archive from
|
||||
https://github.com/JetBrains/kotlin/releases/latest, or installed using various
|
||||
package managers).
|
||||
|
||||
WARNING: if your intention was to run Kotlin/Native compiler, then please use
|
||||
"kotlinc-native" CLI tool instead of "kotlinc". "kotlinc" tool will be removed
|
||||
from Kotlin/Native distribution, so it will stop clashing with "kotlinc" from
|
||||
the main Kotlin distribution.
|
||||
|
||||
""".trimIndent())
|
||||
|
||||
konancMain(utilityArgs)
|
||||
}
|
||||
"cinterop" -> {
|
||||
val konancArgs = invokeInterop("native", utilityArgs)
|
||||
konancArgs?.let { konancMain(it) }
|
||||
|
||||
Reference in New Issue
Block a user