Jump to content
OMRON Forums

Is reading/writing of bits thread safe?


steveneads
 Share

Recommended Posts

Hi

 

Can you tell me whether it's thread safe to read/write to bits located in user shared memory from C code? It seems that to set a bit, I need to read at least one byte, change the required bit and then write the byte back - which needs multiple lines of code. Can another process access this address and change other parts of the byte whilst the read/write is mid way through?

 

If I have a variable defined such as:

ptr myvar->u:user:4.1.1

is reading and writing to this from PMAC script always thread safe too?

 

Thanks

Steve

Link to comment
Share on other sites

  • Replies 13
  • Created
  • Last Reply

Top Posters In This Topic

Hi,

 

You need not worry about individual bytes that you are currently manipulating being overwritten by other processes. You only need to worry about other bytes in the same word being overwritten.

 

In background C programs (NOT realtime/foreground programs), you can use mutexes/semaphores to ensure atomicity.

 

In foreground programs, you need to use something like global flags to lock out certain memory registers before you write to them between programs. Just set a flag high when one program is using the memory, and another flag when another program is using it, and make the flags mutually exclusive so the two programs cannot interfere with one another.

 

If you need more information, please ask.

Link to comment
Share on other sites

In the Script environment, it is possible for the read/modify/write sequence necessary to change a bit or bits of a 32-bit word to be interrupted by a higher-priority task. If the higher-priority task writes to this same word, the lower-priority task will then undo what the higher-priority task has done.

 

To protect against this, we have a process-locking mechanism in the Script environment using non-saved setup element bits Sys.Lock (i=0 to 31). When a Script command reads Sys.Lock and finds it to be 0, it sets it to 1 in an atomic (non-interruptible) operation. So you can do a sequence like this:

 

while (Sys.Lock[8] == 1) {} // Wait for process 8 to be unlocked, then lock it for this task

myvar = 1; // Do read/modify/write operation

Sys.Lock[8] == 0; // Unlock process 8 to release for other tasks

Link to comment
Share on other sites

Hi Curt

 

Thanks - I presume that this locking is just to protect background PLCs from foreground PLCs and motion programs? As I understand it the background PLCs run sequentially as they did in UMAC - so they can't interrupt each other - is that right? Does this also give protection from higher priority tasks such at C code in a user servo or rtiplc or even cfromscript called from a motion program?

 

I still haven't had an answer that I understand about protection in the C environment! Can you help with this please? If I'm using pointers to write to ushm, what read/write size is safe and what isn't?

 

Thanks

Steve

Link to comment
Share on other sites

Hi,

 

To clarify, if you are doing a read-modify-write task, there is no safe "smallest" data size to manipulate. Higher-priority tasks can interrupt the process.

 

In C, if you want to lock out other processes from manipulating the memory that your background C program is manipulating, you can use mutexes or semaphores. Both of these force the current thread to have absolute access to the memory without interruption until the mutex/semaphore is unlocked.

 

Here is a link to a tutorial for using mutexes in Linux:

 

http://www.thegeekstuff.com/2012/05/c-mutex-examples/

 

If you use a mutex lock, the higher-priority threads cannot interrupt your background threads' manipulation of locked memory.

Link to comment
Share on other sites

Steve:

 

You are correct that different tasks of the same priority level in PPMAC cannot interrupt each other; they execute in a round-robin fashion. The potential worry is if you have tasks of different priority levels accessing the same word in a read-modify-write sequence. The potential result is non-intuitive for many people, as it is the lower-priority task that "wins" in the case of a conflict, because when it resumes to finish its sequence after being interrupted, it overwrites what the higher priority task had written to the register.

 

The priority levels in PPMAC are, from highest to lowest:

 

1. Gate3 capture/compare interrupt (rarely used)

2. Phase interrupt

3. Servo interrupt

4. Real-time interrupt

5. Background

 

Usually, the conflicts in user code are between RTI and background. They can occur with either memory or I/O registers, although they would be much more likely to occur with I/O, which generally requires 2 100-nanosecond accesses, so the chance of getting an interrupt is much higher than with memory (which might be a good thing as you are more likely to find the problem early in development).

Link to comment
Share on other sites

I have a simple case which illustrates the issue. This is how I handle reading and writing (read-modify-write) bits in c-code to be as fast as possible:

 

bgcplc.c

#define Get_Bit1 ((((unsigned char *) pushm)[27] >> 1) & 1)
#define Set_Bit1 ((unsigned char *) pushm)[27] |= 1 << 1
#define Rst_Bit1 ((unsigned char *) pushm)[27] &= ~(1 << 1)

void user_plcc()
{
// Random delays to stir things up
struct timespec mSecSleeper = { 0 };
mSecSleeper.tv_nsec = randx(100);

// Set, wait, check, log error, reset, wait, check, log error
Set_Bit1;
nanosleep(&mSecSleeper, NULL);
if (Get_Bit1 != 1) pshm->P[3]++;
Rst_Bit1;
nanosleep(&mSecSleeper, NULL);
if (Get_Bit1 != 0) pshm->P[3]++;

// Log running
pshm->P[1]++;
}

 

rticplc.c

void realtimeinterrupt_plcc()
{
// Random delays to stir things up
struct timespec mSecSleeper = { 0 };
mSecSleeper.tv_nsec = randx(100);

// Set, wait, check, log error, reset, wait, check, log error
Set_Bit2;
nanosleep(&mSecSleeper, NULL);
if (Get_Bit2 != 1) pshm->P[2]++;
Rst_Bit2;
nanosleep(&mSecSleeper, NULL);
if (Get_Bit2 != 0) pshm->P[2]++;

// Log running
pshm->P[0]++;
}

 

I see P2 going up (i.e. RTI loses as explained above). I understand mutexes. Where would I create one?

Link to comment
Share on other sites

Hi Curt/Charles

 

I think that I've a better understanding now - I need to be careful that high priority tasks don't interrupt a read-modify-write sequence being performed by a background task. However, if I'm writing data from a background PLC, can you confirm what size of variable is safe? In C code I believe that I can write a byte, 2 bytes, 4 bytes, or 8 bytes directly, but I'd need to do a read-modify-write to write a bit. Is this the same for background PLCs? How does your code handle variables of different sizes? Is it only variables that are less than a byte long which are a risk, or are longer variables also handled with a read-modify-write by PMAC?

 

Thanks

Steve

Link to comment
Share on other sites

Fundamentally, all memory and I/O accesses by the Power PMAC processor are 32-bit or 64-bit. The actual write operation is atomic -- it cannot be interrupted. (For 64-bit registers, the register must be accessed by a single 64-bit write [e.g. to a "double" variable] for the operation to be atomic. And yes, one Power PMAC user found this out the hard way...)

 

If your operation to change the value of a register requires you first to read the present value of the register, modify some bits of the register, then write back to the register, this is never an atomic operation, no matter how many of the bits of the register you are dealing with.

 

In the Script environment, the complexity of the read-modify-write sequence is often hidden from you. If you write to a "partial-word" data structure element or "partial-word" user-defined M-variable, this action automatically incorporates a read-modify-write operation (that is interruptible).

 

In the C environment operating on I/O, you must explicitly access a full 32-bit register, and write your own masking code to modify the register. In the C environment operating on memory, if you access a partial-word data structure element, the compiler will generate the code to do the read-modify-write operation, but in the Power PMAC processor, this is not an atomic operation.

 

Also, if your operation requires multiple write operations (32-bit or 64-bit), this is never an atomic operation. If you need to ensure a coherent set of data across the multiple registers, you must ensure that this overall operation is not interrupted by anything that could destroy that coherence.

Link to comment
Share on other sites

Hi Curt

 

For me the issue is your statement:

 

"In the Script environment, the complexity of the read-modify-write sequence is often hidden from you. If you write to a "partial-word" data structure element or "partial-word" user-defined M-variable, this action automatically incorporates a read-modify-write operation (that is interruptible)."

 

What is a "partial-word" in your terminology in this statement, is a word 32 bits? I presume that PPMAC is a 32 bit processor?

 

Is my time critical code going to run faster if I only define M variables in USHM as 32 bits wherever possible?

 

Where are the 64 bit registers in PPMAC that your user had problems with? - I've only spotted 32 bit addresses.

 

Thanks

Link to comment
Share on other sites

Yes, a word is 32 bits. So any M-variable of less than 32-bits is written to with a read-modify-write sequence.

 

The follow-on is yes, using 32-bit M-variables is faster. Let's say you had 20 Boolean flags you wanted to store in USHM. Speed-wise you would be better off declaring 20 32-bit variables, even though you would only actually be using one bit of each.

 

The PPMAC processor does have atomic 64-bit access instructions, which the compiler uses when accessing "double" variables. So, for example, if you access one of our P-variables, or a Ddata register in USHM, in your C-code, it cannot be interrupted in the middle. (The same is true with our Script access to double registers like this.)

 

I don't remember the exact details, but basically the user I was talking about was trying to copy logged 64-bit floating-point variable values from one place to another using 2 separate 32-bit accesses in C. Occasionally the source value was being overwritten in between the two accesses and he ended up with an incoherent value. If you really want to, you can find the old thread somewhere on this forum. It was a couple of years ago, I think.

 

Since you asked about execution speed, I want to point out something that is non-intuitive to many. In the Script environment, to keep users from needing to perform explicit type-matching of variables as you would have to do in C, the Script execution engine automatically converts everything to double-precision floating-point format (64 bits in Power PMAC, 48 bits in older PMACs). Therefore, when it comes time to store the resulting value, the quickest operation is to store it in a 64-bit floating point variable (e.g. P, Q, Ddata), because that requires no format conversion (which takes longer than the second 32-bit access). Of course, the tradeoff is inefficient use of memory.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
 Share


×
×
  • Create New...