2

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
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
WhiteSpidy.
  • 1,107
  • 1
  • 6
  • 28
  • One option would be to write an interface in shared code, and implement the generated protocol in swift. That way you'll get round the funky kotlin native concurrency. Often interface/impls are a cleaner approach to application code rather than expect/actual. – enyciaa Aug 02 '21 at 21:29

2 Answers2

4

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.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Hey, Thanks for this ```completionHandler.freeze()``` worked. But I didn't understand what happens here. Does it means that KMM has created a seperate thread for uploading task and my iOS code is in seperate thread due to which I am not able to use my lambda function variables ? Or is it something else ? – WhiteSpidy. Aug 03 '21 at 09:49
1

Add below to gradle.properties

kotlin.native.binary.memoryModel=experimental
kotlin.native.binary.freezing=disabled
Saba
  • 1,208
  • 14
  • 19
  • Incorporating the experimental memory model involves much more detail than is mentioned in this answer, and is described in detail in [New memory model migration guide](https://github.com/JetBrains/kotlin/blob/master/kotlin-native/NEW_MM.md). I see no point in duplicating it here and my reply already contains that link. Besides, it is a bad practice to suggest such a crude technology without at least explaining that it's not yet recommended to be used in production. – Phil Dukhov Apr 16 '22 at 11:18