ballerinaisolation

How to avoid `invalid access of mutable storage in an 'isolated' function`?


I have an abstract type which is being used in few concrete objects and a record type that contains a list of that abstract type.

public type AbstractType object {
    isolated function profile() returns string;
};

public isolated class ConcreteImpl1 {
    *AbstractType;
    isolated function profile() returns string {
        return "concreteOne";
    }
}
public isolated class ConcreteImpl2 {
    *AbstractType;
    isolated function profile() returns string {
        return "concreteTwo";
    }
}

public type ImplHolderType record {
    AbstractType[] implementations;
};

I have initialized ImplHolderType with 2 objects and trying to access one of the values inside an isolated function.

final ImplHolderType implList = {
    implementations: [new ConcreteImpl1(), new ConcreteImpl2()]
};

isolated resource function get sample/profile() returns string {
        AbstractType abstractType = implList.implementations[0];
        return abstractType.profile();
}

Here I'm getting an error when I'm trying to access the list (implList.implementations[0]) saying invalid access of mutable storage. Can you suggest a workaround that I can do to avoid this error?


Solution

  • Since implList is mutable (even though it is final, the fields can still be updated), you can't access it within an isolated function. You could make implList an isolated variable and then access it within the isolated function. You'll have to use a lock statement when accessing.

    isolated ImplHolderType implList = {
        implementations: [new ConcreteImpl1(), new ConcreteImpl2()]
    };
    
    service on new http:Listener(8080) {
        isolated resource function get sample/profile() returns string {
            lock {
                return implList.implementations[0].profile();
            }
        }
    }
    

    https://ballerina.io/learn/by-example/isolated-variables

    https://ballerina.io/learn/by-example/#concurrency-safety

    Alternatively, if your implementation classes won't have any mutable fields, you can make them readonly classes, and then make implList be of type readonly & ImplHolderType, which will make it be immutable. Then you would be able to access it in an isolated function without a lock statement.

    import ballerina/http;
    
    public type AbstractType object {
        isolated function profile() returns string;
    };
    
    public readonly class ConcreteImpl1 {
        *AbstractType;
        isolated function profile() returns string {
            return "concreteOne";
        }
    }
    
    public readonly class ConcreteImpl2 {
        *AbstractType;
        isolated function profile() returns string {
            return "concreteTwo";
        }
    }
    
    public type ImplHolderType record {
        AbstractType[] implementations;
    };
    
    final readonly & ImplHolderType implList = {
        implementations: [new ConcreteImpl1(), new ConcreteImpl2()]
    };
    
    service on new http:Listener(8080) {
        isolated resource function get sample/profile() returns string {
            return implList.implementations[0].profile();
        }
    }