Page 1 of 2
NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 15:30
by HaWe
hi,
what is happening in the following case:
e.g., if I have two independent tasks (threads) D and P.
task D() is reading sensor data and collecting other data and stores them intermediately into long global arrays,
task P() processes data; it is reading global array values and processes them .
what happens if D wants to write array values just when P isn't finished reading yet?
will D wait until they are released by P and write them immediately after? Or will D drop the data and write new (different) data in the next round?
what happens if D has just begun writing them but is not yet finished, but just in that moment P wants to read them?
will P wait until D is finished writing them and then read immediately after? Or is P reading nothing or any garbage, will it keep the old data or change it to anything ?
Same question, other way round: what happens if both tasks want to read or both want to write a long array at almost the same time which lasts longer than the current timeslice lasts? Will the earlier timeslice proceed until it is finished or will it interrupt the read or write action?
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 16:11
by afanofosc
I don't know if I really understand what you are asking.
If you want to enforce single-task access to a shared resource (array, motor, sensor, whatever) then you protect access to that resource with a global mutex variable which you acquire and release. If two tasks are writing to an array you probably should protect the write operation with a mutex. You could do that by putting the write operation into a shared function that is marked as safecall or by wrapping the write operations in each task in acquire/release pairs.
If two tasks are reading from an array then there is no need to protect the resource. If one is writing and the other is only reading there is no need to protect access to the resource. Your reader will either get the new value or the old value depending on whether it reads an element before it is written or after it is replaced with a new value. You can't get any data corruption or anything of that sort with the NXT VM by writing/reading at the same time.
Technically it is also perfectly safe VM-wise to have two threads writing at the same time since each task will be executing an NBC-level opcode one at a time and never simultaneously. The temporary compiler variables used by each task are unique to each task so there's no chance for an array operation in NXC on one task to be mangled half-way through by another task modifying an array index value or something like that. You could conceivably adjust the size of an array to a shorter length in one task and cause a second task that expects it to have more elements to write past the end of it or read past the end of it but that's really just a bug in your code if you do that.
But if you want for the reading task to completely read all elements from an array before any of them get changed by another task then you will need to implement resource protection using acquire and release calls (unless the "read" operation is simply a copy from the global shared array to a local temporary array).
John Hansen
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 16:43
by HaWe
ok, I wanted to keep the question sort of "simple but universally", actually it's the following:
All following tasks are running on a BT slave.
The global arrays is
long Array[13]
task D
is continously reading 3 sensor DATA (mostly analog) and (in future) 2 motor encoder values and writes them into the global array into positions 0-4.
this task is running quite quickly for refreshing the sensor values. Most data are time-critical and must not get corrupted or dropped.
task M
is controlling MOTORS on demand and writes motor run state flags into the global array into positions 5.
When the mission is finished, the task stops.
As this tasks writes each value only once and then never, the data are not time-critical but are of high importance, so they must not get corrupted or dropped.
task N
similar to M, writes motor run state flags into the global array into positions 6.
task C
is started on demand,
it calculates CHESS moves
and if it's finished it writes them into this global array into positions 7 -12
this task writes only very seldom, just
- once at the start for signalizing that it's busy,
- once quite at the end when the move calculation is finished to provide all move values and flags (in particular also the "auto_move_ready flag").
Then this tasks stops.
As this tasks writes each set of values only once and then never, the data are not time-critical but are of high importance, so they must not get corrupted or dropped.
task P
is continously reading the whole array, and PROCESSES the data into a string:
it's reading array, the decimal length of i 's value, formats and transforms the values into appropriate strings, and daisy chains all string data in the form
string out = len0data0len1data1len2data2len3data3len4data4.........
when finished, it sends the string via BT and waits for acknowledge, then begins anew.
This lasts maybe 3 ms for array/string processing and about 20 ms for BT send/response.
So this task is running intermediatly quick to send time-sensitive data as soon as possible so that the master can react to critical situations, any data must not get corrupted or dropped neither by processing nor by sending.
task R
is continuously READING REMOTE BT strings but this does not interfere with the other values or strings or messages, it just it interferes and sometimes messes up other BT send/receive actions.
E.g., it starts tasks C, M, or N on demand of the master brick or stops motor tasks intermediately in critical situations - or stops the chess task if a +INFINITY move (like check mate) already has been found by another brick. So it is important as it partially contains data which are either time critical or very important which must not get corrupted or dropped.
task V
is continously VISUALIZING some of the values of interest on the display (wait(10)), not time-critical, but the values must be reliable for error checking.
So if I understand you correctly, all tasks should aquire mutexes in the moment they write any data to the global array (D,M,N,C), but there is no need for it for reading tasks (P,V,R)?
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 21:07
by afanofosc
If the array size never changes and each of your tasks never write to elements that another task is writing to then you should change it to use a structure with 13 fields and just write directly to each field in whichever task needs to write to the whichever field and then send it over bluetooth as a binary object and then restore it on the receiving NXT into the structure with 13 fields. You don't need any mutexes regardless of whether you stick with an array of longs or change to a structure (much, much better approach) with each field having the appropriate length for the type of data you are writing to it. When you send it over bluetooth you should just FlattenVar() your structure (or array of 13 longs) into a string and then send the string like you are sending your current string. On the receiving end read the string like you are currently reading the string and then UnflattenVar() it back into a structure (or an array of 13 longs if you insist).
John Hansen
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 22:24
by HaWe
I was afraid flatten would probably cause too long strings compared to my version.
I will never change the size of an array but it may happen in future though that 1 cell might be written by overall 2 independend tasks.
I'm using this BT message system also for bigger sensor arrays in another BT mux project as well
e.g., I have 16 values like
8 touch sensor vaues (0 or 1) = 1 digit each => 8 digits
3 long values (-70000...+70000) = 6 digits each 0 => 18 digits
4 char values -1...129 = 3 digits each => 12 digits
1 int value 0...1024 => 1* 4 digits
my string looks like
1b1b1b1b1b1b1b1b6aaaaaa6bbbbbb6cccccc3vvv3www3uuu3vvv4iiii
=> len= 56 (8+8+18+3+12+3+4+1)
how long would be the length of the flattened string of a
long array[16]?
1 long=64bit=8 byte
I was afraid of a string of 16*8=128 bytes
- considering that I may send only a BT string of max length=58?
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 22:55
by mattallen37
With the NXT, long is 32 bits, not 64.
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 23:00
by HaWe
aah - ok, now that makes sence!
Re: NXC: multithreading: variable access conflict?
Posted: 22 Feb 2012, 23:09
by HaWe
ps:
but 1 long=32 bit= 4 byte gives
16*4 = 64 char length, that's still too much if a BT string can have only 56 or 58 at most!
I would have to drop the last 2 values of my 16, so it's just an array of 14 long which I could use at the most in my other project.
Re: NXC: multithreading: variable access conflict?
Posted: 23 Feb 2012, 01:15
by mattallen37
Well, are you taking full advantage of the data? I mean, if you only need 20 bit numbers, then you can use the other 12 bits for other data. Do you really need to be able to send a full 512 bits? I suggest you try compacting it as much as you can.
If you really do need to send 512 bits, then just split it into two messages (or use RS485 which has 128 byte buffers).
Re: NXC: multithreading: variable access conflict?
Posted: 23 Feb 2012, 01:51
by afanofosc
My point was that most of your data does not require the same long data type.
8 touch sensor vaues (0 or 1) = 1 digit each => 8 digits
3 long values (-70000...+70000) = 6 digits each 0 => 18 digits
4 char values -1...129 = 3 digits each => 12 digits
1 int value 0...1024 => 1* 4 digits
A structure containing 8 byte fields, 3 long fields, 4 char fields, and 1 int field would take a total of 8+12+4+2 bytes plus the null terminator at the end == 27 bytes. 16 longs would flatten to 65 bytes so it would be too big for the mailbox system but the size of the bluetooth input buffer is 128 bytes and the BluetoothWrite system call function (CommBTWrite) actually lets you write 256 bytes in a single call. You'd have to be really clever to process the raw bt data on the receiving end if you chose to not use the mailbox system (i.e., kind of like reading bluetooth GPS data in NXC)
John Hansen