Ace Rimmer wrote:When I say 'not in that order', I mean I was doing both things (getting 'new' unit data and placing in tables, and searching those tables in the same tick).
...
I assumed the startlongtask/yieldlongtask would allow both functions (getnewdata/searchvisibleunits) at the same time without issues.
Yeah, doing that could potentially cause a whole load of problems. The only time it's safe to simultaneously operate multiple tasks on the same set of data (i.e. a table) is if the tasks are only reading values, otherwise you could end up deleting a value in one task while the other is about to try read from it.
For example (borrowing some syntax from Handel-C):
Code: Select all
//update
function task1(data) {
print("deleting value");
data["a"] = null; //delete element
}
//read
function task2(data) {
for (key in data) {
print("this is...");
print(key);
}
}
//main
function tick() {
data = ["a":6, "b":5, "c":4, "d":3, "e":2, "f":1];
//execute task functions in parallel
par {
task1(data);
task2(data);
}
}
If you imagine each statement takes the same amount of time to execute (1 clock cycle) then things will look like this:
clock 0: Enter tick() function.
clock 1: data is defined.
clock 2: Enter task1() and task2() functions simultaneously.
clock 3: task1() prints message. task2() enters for loop with key referring to data["a"].
clock 4: task1() deletes data["a"]. task2() prints message.
clock 5: task2() references data["a"] which is null/deleted.
This is assuming data is passed-by-reference. I haven't checked but arguments in Lua are most likely passed-by-value so this shouldn't be the exact kind of thing to worry about as each task would be working on it's own copy of data. However, if you're using global variables then you would very easily hit this problem, which is why you should avoid using them. For example don't do:
Code: Select all
//global
data = ["a":6, "b":5, "c":4, "d":3, "e":2, "f":1];
//update
function task1(data) {
print("deleting value");
data["a"] = null; //delete element
}
//read
function task2(data) {
for (key in data) {
print("this is...");
print(key);
}
}
//main
function tick() {
//execute in parrallel
par {
task1();
task2();
}
}
clock 0: data is defined.
clock 1: Enter tick() function.
clock 2: Enter task1() and task2() functions simultaneously.
clock 3: task1() prints message. task2() enters for loop with key referring to data["a"].
clock 4: task1() deletes data["a"]. task2() prints message.
clock 5: task2() references data["a"] which is null/deleted.
Two possible solutions. First, make sure your update and read tasks are executed sequentially (nice and easy). Second, implement channels which would allow parallel tasks to pass information between each other (tricky and could lead to blocking as a task would have to wait for communication from the channel).
While not entirely relevant it may help to read about ACID to highlight some of the issues, in particular, the bit about isolation.