APIs for easier freeze debugging. (#1783)

This commit is contained in:
Nikolay Igotti
2018-07-10 14:28:56 +03:00
committed by GitHub
parent 7e517ef6f1
commit da7e31a10a
7 changed files with 79 additions and 9 deletions
+6
View File
@@ -692,6 +692,12 @@ task freeze3(type: RunKonanTest) {
source = "runtime/workers/freeze3.kt"
}
task freeze4(type: RunKonanTest) {
disabled = (project.testTarget == 'wasm32') // No exceptions on WASM.
goldValue = "OK\n"
source = "runtime/workers/freeze4.kt"
}
task atomic0(type: RunKonanTest) {
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
goldValue = "35\n" + "20\n" + "OK\n"
@@ -0,0 +1,24 @@
package runtime.workers.freeze4
import kotlin.test.*
import konan.worker.*
data class Data(val x: Int, val s: String, val next: Data? = null)
@Test fun runTest() {
val data1 = Data(1, "")
data1.freeze()
assertFailsWith<FreezingException> {
data1.ensureNeverFrozen()
}
val dataNF = Data(42, "42")
dataNF.ensureNeverFrozen()
val data2 = Data(2, "2", dataNF)
assertFailsWith<FreezingException> {
data2.freeze()
}
assert(!data2.isFrozen)
println("OK")
}
+24 -6
View File
@@ -223,7 +223,6 @@ constexpr const char* MemoryStatistic::indexToName[];
#endif // COLLECT_STATISTIC
struct MemoryState {
#if TRACE_MEMORY
// Set of all containers.
ContainerHeaderSet* containers;
@@ -419,7 +418,7 @@ RUNTIME_USED ContainerHeader theStaticObjectsContainer = {
void objc_release(void* ptr);
void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
RUNTIME_NORETURN void ThrowFreezingException();
RUNTIME_NORETURN void ThrowFreezingException(KRef toFreeze, KRef blocker);
} // extern "C"
@@ -1536,11 +1535,18 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
* - not 'marked' and not 'seen' as WHITE marker (object is unprocessed)
* When we see GREY during DFS, it means we see cycle.
*/
void depthFirstTraversal(ContainerHeader* container, bool* hasCycles, KStdVector<ContainerHeader*>& order) {
void depthFirstTraversal(ContainerHeader* container, bool* hasCycles,
KRef* firstBlocker, KStdVector<ContainerHeader*>& order) {
// Mark GRAY.
container->setSeen();
traverseContainerReferredObjects(container, [hasCycles, &order](ObjHeader* obj) {
traverseContainerReferredObjects(container, [hasCycles, firstBlocker, &order](ObjHeader* obj) {
if (*firstBlocker != nullptr)
return;
if (obj->has_meta_object() && ((obj->meta_object()->flags_ & MF_NEVER_FROZEN) != 0)) {
*firstBlocker = obj;
return;
}
ContainerHeader* objContainer = obj->container();
if (!objContainer->permanentOrFrozen()) {
// Marked GREY, there's cycle.
@@ -1548,7 +1554,7 @@ void depthFirstTraversal(ContainerHeader* container, bool* hasCycles, KStdVector
// Go deeper if WHITE.
if (!objContainer->seen() && !objContainer->marked()) {
depthFirstTraversal(objContainer, hasCycles, order);
depthFirstTraversal(objContainer, hasCycles, firstBlocker, order);
}
}
});
@@ -1687,8 +1693,12 @@ void FreezeSubgraph(ObjHeader* root) {
// Do DFS cycle detection.
bool hasCycles = false;
KRef firstBlocker = nullptr;
KStdVector<ContainerHeader*> order;
depthFirstTraversal(rootContainer, &hasCycles, order);
depthFirstTraversal(rootContainer, &hasCycles, &firstBlocker, order);
if (firstBlocker != nullptr) {
ThrowFreezingException(root, firstBlocker);
}
// Now unmark all marked objects, and freeze them, if no cycles detected.
if (hasCycles) {
freezeCyclic(rootContainer, order);
@@ -1743,4 +1753,12 @@ OBJ_GETTER(ReadRefLocked, ObjHeader** location, int32_t* spinlock) {
return value;
}
void EnsureNeverFrozen(KRef object) {
if (object->container()->frozen())
ThrowFreezingException(object, object);
// TODO: note, that this API could not not be called on frozen objects, so no need to care much about concurrency,
// although there's subtle race with case, where other thread freezes the same object after check.
object->meta_object()->flags_ |= MF_NEVER_FROZEN;
}
} // extern "C"
+5 -1
View File
@@ -246,6 +246,8 @@ struct MetaObjHeader {
const TypeInfo* typeInfo_;
// Strong reference to counter object.
ObjHeader* counter_;
// Flags for object state.
int32_t flags_;
#ifdef KONAN_OBJC_INTEROP
void* associatedObject_;
@@ -470,7 +472,9 @@ OBJ_GETTER(AdoptStablePointer, void*) RUNTIME_NOTHROW;
// Check mutability state.
void MutationCheck(ObjHeader* obj);
// Freeze object subgraph.
void FreezeSubgraph(ObjHeader* root);
void FreezeSubgraph(ObjHeader* obj);
// Ensure this object shall block freezing.
void EnsureNeverFrozen(ObjHeader* obj);
#ifdef __cplusplus
}
#endif
+4
View File
@@ -61,6 +61,10 @@ enum Konan_TypeFlags {
TF_IMMUTABLE = 1 << 0
};
enum Konan_MetaFlags {
MF_NEVER_FROZEN = 1 << 0
};
// Extended information about a type.
struct ExtendedTypeInfo {
// Number of fields (negated Konan_RuntimeType for array types).
+4
View File
@@ -577,4 +577,8 @@ KBoolean Kotlin_Worker_isFrozenInternal(KRef object) {
return object == nullptr || object->container()->permanentOrFrozen();
}
void Kotlin_Worker_ensureNeverFrozen(KRef object) {
EnsureNeverFrozen(object);
}
} // extern "C"
@@ -21,7 +21,8 @@ import konan.internal.ExportForCppRuntime
/**
* Exception thrown whenever freezing is not possible.
*/
public class FreezingException() : RuntimeException()
public class FreezingException(toFreeze: Any, blocker: Any) :
RuntimeException("freezing of $toFreeze has failed, first blocker is $blocker")
/**
* Exception thrown whenever we attempt to mutate frozen objects.
@@ -41,6 +42,14 @@ fun <T> T.freeze(): T {
val Any?.isFrozen
get() = isFrozenInternal(this)
/**
* This function ensures that if we see such an object during freezing attempt - freeze fails and FreezingException
* is thrown. Is object is already frozen - FreezingException is thrown immediately.
*/
@SymbolName("Kotlin_Worker_ensureNeverFrozen")
external fun Any.ensureNeverFrozen()
@SymbolName("Kotlin_Worker_freezeInternal")
internal external fun freezeInternal(it: Any?)
@@ -48,7 +57,8 @@ internal external fun freezeInternal(it: Any?)
internal external fun isFrozenInternal(it: Any?): Boolean
@ExportForCppRuntime
internal fun ThrowFreezingException(): Nothing = throw FreezingException()
internal fun ThrowFreezingException(toFreeze: Any, blocker: Any): Nothing =
throw FreezingException(toFreeze, blocker)
@ExportForCppRuntime
internal fun ThrowInvalidMutabilityException(where: Any): Nothing = throw InvalidMutabilityException(where)