#Should I use lock statement within async-calls while modifying module-level-variables?

1 messages · Page 1 of 1 (latest)

storm fossil
#

1)If I have a module level variable(array and other primitive types)
AFAIU we don't need to use a lock as we are using same thread.

string[] menu = ["pizza", "hot dog", "fries"];
public function main() {
  
    while true {
        if (//some breaking condition) {
            break;
        }
        
        future<int> f = start modifyMenu(//some string);
    } 
}

function modifyMenu(string dish) {
    // Do we need below lock??
   lock {
       menu.push(dish);
   }
}

2)If I have a module level object and it is modified with a method(array and other primitive types)

Data data = new();

class Data {
  string[] datasets = [];

  function append(string string) {datasets.push(string);}
}

public function main() {
    decimal now = time:monotonicNow();

    while true {
        if (//some breaking condition) {
            break;
        }
        
        future<int> f = start modifyMenu(//some string);
    }
}

function modifyMenu(string dish) {
    // Do we need below lock??
   lock {
       data.append(dish);
   }
}

Please explain isolation objects , isolation methods.. How to use them for above situation??

coarse prairie
#

Looping in @fickle sparrow also.

  1. IMO, it depends on the logic - e.g., if there's a construct/call that may cause the strand to yield. Also note that with isolated functions, strands created by start actions or worker declarations can run on different threads (although not explicitly specified) - https://ballerina.io/downloads/swan-lake-release-notes/swan-lake-2201.2.0#support-for-running-new-strands-safely-on-separate-threads.

  2. Similar to 1. If data can be accessed concurrently, you can use isolated to ensure concurrency safety. By marking them as isolated you enable some compiler checks that will also ensure that future changes are validated to be safe. An isolated object ensures that any mutable state is accessed only within the object and within lock statements. The lock statements will be within the object methods. If you use a final variable of an isolated object (or immutable) type, you wouldn't have to use locks when accessing the variable since the isolated object ensures safety. Alternatively, you can have isolated variables (when not final and/or not immutable/an isolated object) , and you'd have to use lock statements when accessing them (enforced by the compiler). There are some additional restrictions that are enforced to guarantee safety too. See https://ballerina.io/learn/distinctive-language-features/concurrency/#concurrency-safety.

Let’s now look at how concurrency and transactions are handled in Ballerina.

A programming language for the cloud that makes it easier to use, combine, and create network services.

storm fossil
#
  1. What is the difference between using lock alone and using isolated & lock ```string[] menu = ["pizza", "hot dog", "fries"];
    public function main() {

    while true {
    if (//some breaking condition) {
    break;
    }

     future<int> f = start modifyMenu(//some string);
    

    }
    }

function modifyMenu(string dish) {
lock {
menu.push(dish);
}
} vsisolated string[] menu = ["pizza", "hot dog", "fries"];
public function main() {

while true {
    if (//some breaking condition) {
        break;
    }
    
    future<int> f = start modifyMenu(//some string);
} 

}

function modifyMenu(string dish) {

lock {
menu.push(dish);
}
}Here, `menu` has isolated in front of it. 2) https://github.com/ballerina-platform/ballerina-spec/blob/master/lang/proposals/isolated/isolated.md#other-methods here we have to use `clone()` to return a copy `self.x[i]` . What should I do if I have a object array as the field and that needs to be returned.isolated class ReportData {
private Result[] passed = [];
private Result[] failed = [];
private Result[] skipped = [];

isolated getData() returns Result[] {
lock{
return passed.clone(); // doesn't work
}
}

GitHub

Ballerina Language and Platform Specifications. Contribute to ballerina-platform/ballerina-spec development by creating an account on GitHub.

coarse prairie
#
  1. when you use isolated, the compiler enforces using lock statements wherever applicable. For example, if someone adds another function in the future without using locks when accessing menu, if isolated is used, they will get a compilation error, whereas they won't get the error if menu is not an isolated variable.

E.g.,

function clearMenu() {
    menu.removeAll();
}

Isolated variables and isolated objects are also isolated roots - any and all mutable state accessible via the isolated root is guaranteed to be accessible only via the isolated root. This is maintained via additional constraints on transferring values in and out (which are also enforced with constructs marked as isolated).

Consider an isolated variable isolated string[][] names, whatever mutable string[] value accessed via names is guaranteed to be accessible only via names, therefore we can guarantee safety by requiring lock statements when accessing names.

#
  1. you can't use clone (or cloneReadOnly) with a mutable object. If you need to pass an object in/out of a lock statement with restricted access it needs to be

i. an immutable object (in which case you can call clone on a list of it)

isolated class ReportData {
    private Result[] passed = [];
    private Result[] failed = [];
    private Result[] skipped = [];

    isolated function getData() returns Result[] {
        lock {
            return self.passed.clone();
        }
    }
}

readonly class Result {
    string name;

    function init(string name) {
        self.name = name;
    }
}

ii. or it needs to be an isolated object in which case you can create and return a new list

isolated class ReportData {
    private Result[] passed = [];
    private Result[] failed = [];
    private Result[] skipped = [];

    isolated function getData() returns Result[] {
        Result[] passed = [];
        lock {
            foreach [int, Result] [index, result] in self.passed.enumerate() {
                passed[index] = result;
            }
        }
        return passed;
    }
}

isolated class Result {
    private string name;

    function init(string name) {
        self.name = name;
    }
}
```

There's an open issue to see how we can simplify ii.