Search code examples
androidiosswiftkotlin-multiplatformawss3transferutility

KMM IncorrectDereferenceException in Thread


I have been trying to upload an image to aws s3 server using shared module KMM. It works very well in Android but in iOS I have been getting this issue :- Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared

Now as much as I have searched about this I got to know it is related to something with frozen() but I am not sure what it is and how can I resolve this.

Code :-

actual class ClassName {

    init {
        ensureNeverFrozen()
    }

    actual fun imageUpload() {

        var credentialsProvider = AWSCognitoCredentialsProvider(regionType = // Region here, identityPoolId = //identityPoolId here)

        var configuration = AWSServiceConfiguration(region =  // Region here, credentialsProvider = //credentialsProvider here)

        AWSServiceManager.defaultServiceManager()?.defaultServiceConfiguration = configuration

        val expression = AWSS3TransferUtilityUploadExpression()

        // Start uploading using AWSS3TransferUtility
        val awsTransferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
            val completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock
            completionHandler = { _: AWSS3TransferUtilityUploadTask?, error: NSError? ->
                if (error == nil) {
                    val url = AWSS3.defaultS3().configuration.endpoint()?.URL()
                    val publicURL = url?.URLByAppendingPathComponent("bucketName")?.URLByAppendingPathComponent("fileName")
                    // Image Upload Complete
                } else {
                    // Image Upload failure
                }
            }
            awsTransferUtility.uploadFile(
                fileUrl!!,
                bucket = "bucketName",
                key = "fileName",
                contentType = ".image",
                expression = expression,
                completionHandler = completionHandler. // Error pointed on this line
            )
    }
}

Now as soon as I call the function my app gets crashed pointing error to the completionHandler.

Error log :-

Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared ClassName.$imageUpload$lambda-1$lambda-0$FUNCTION_REFERENCE$1@2803dc8 from other thread
    at 0   iosApp                              0x000000010cc1984f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
    at 1   iosApp                              0x000000010cc138cd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93
    at 2   iosApp                              0x000000010cc139fd kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93
    at 3   iosApp                              0x000000010cc327fd kfun:kotlin.native.IncorrectDereferenceException#<init>(kotlin.String){} + 93
    at 4   iosApp                              0x000000010cc3461f ThrowIllegalObjectSharingException + 623
    at 5   iosApp                              0x000000010cd16fc2 _ZN12_GLOBAL__N_128throwIllegalSharingExceptionEP9ObjHeader + 34
    at 6   iosApp                              0x000000010cd170fd _ZN12_GLOBAL__N_136terminateWithIllegalSharingExceptionEP9ObjHeader + 13
    at 7   iosApp                              0x000000010cd1af0a _ZNK16KRefSharedHolder3refIL11ErrorPolicy3EEEP9ObjHeaderv + 58
    at 8   iosApp                              0x000000010cbf53ca _ZL39Kotlin_Interop_unwrapKotlinObjectHolderP11objc_object + 42
    at 9   iosApp                              0x000000010cbee050 _4b4d4d4c69623a736861726564_knbridge15 + 224
    at 10  AWSS3                               0x000000010d84509e -[AWSS3TransferUtility URLSession:task:didCompleteWithError:] + 4814

Solution

  • A native concurrency model available for preview. Check out New memory model migration guide. After release you shouldn't face any such problems, but until then the above answer is valid.


    Try to call completionHandler.freeze()

    Alternatively, move handler to function call(without storing it in a variable).

    If inside the handler you're using some variables from outer scope, they may need to be frozen too. If none of first two methods works, try replacing content of the completion with just print() to see if it helps, and if it does - localize the problematic line by uncommenting parts of code one by one.

    KMM concurrency model forbids accessing to mutable object from different threads, and freeze makes object non mutable so it can be used from different threads.

    With coroutines objects gets frozen automatically when needed, but when you're switch threads without coroutines, you have to do it by your hands.

    That's exactly what's happening here: AWS calls completionHandler from an other thread(which is quite usual for methods with completion)

    Check out more about concurrency model here: https://kotlinlang.org/docs/mobile/concurrency-overview.html

    This behaviour is what we had to manage with KMM for now, but soon it will be changed, this is the main blocker KMM to go from alpha to release, and the JetBrains team is focused on solving this particular problem so we don't have to use freeze() anymore.