IPC: Difference between revisions
Line 65: | Line 65: | ||
}else{ //If we are done waiting re-attaining lock, we start to read data (assign it to an output is this case) <br/> | }else{ //If we are done waiting re-attaining lock, we start to read data (assign it to an output is this case) <br/> | ||
for(int i=0; i<8; i++){<br/> | for(int i=0; i<8; i++){<br/> | ||
y[i] = shm_client.getData(i,0,array_index); | y[i] = shm_client.getData(i,0,array_index);//Reading and writing is done using functions usage: getData(row,column,array_index) <br/> | ||
}<br/> | }<br/> | ||
}<br/> | }<br/> |
Revision as of 17:01, 12 September 2013
For our project, the Football Table a synchronized high speed inter process communication(IPC) was necessary. For us to allow communication between our Motion Loop (in Simulink) and Gazebo. For simulation purposes we ideally want to run as fast possible, whilst retaining a causal link.
The library/communication makes use of the POSIX pthread library. Pthread was chosen for its compatibility with Matlab. For this work a very basic wrapper library was made for this project to deal clean up recurring parts of code. This library basically dumbs pthreads down a bit, making it more accesible. For advanced use i do not recommend using this. In our 'library' we used shared memory in combination with a mutex and a condition. For the sake of usability/adaptability this article will very shortly first discuss the basic principles used.
Shared memory is one of the fastest methods available for inter process communication. Basically a segment of memory gets allocated to which multiple threads/programs have access. This way the processes can exchange data and synchronize with each other. Using shared memory however, is slightly tricky because a simultaneous read/write will cause undefined behavior (most likely a crash). To manage this, mutexes are used.
Mutexes
A mutex, or mutual exclusion, does exactly what its name describes. It provides a thread/program with exclusive access to a shared resource. It attains this exclusive access by 'locking' the mutex. In our case, we use it to control access to shared memory. As long as one process has a lock, no other process can access the data (using a basic mutex), hence ensuring thread-save behavior. There are other types of mutexes which operate slightly different (such as a readers-writers lock). By default if a process cannot get a lock, it will wait until it does. If you do not carefully programs you can easily get deadlocks.
Conditions
Synchronizing programs/threads one program might not be able to continue without data provided by the other. Conditions are a wonderful tool to perform this type of synchronization. They are always used in combination with a mutex and some condition variable (which can be anything). It's use is best described with an short example:
Process 1 will lock the shared memory, check if a condition is met, find out is not (process 2 has not updated data yet). It will wait (on a condition_signal) and automatically unlock the mutex. Once process 2 is ready to update it: locks the mutex, updates the data, signals process 1 (waking it) and unlocks the mutex. This is summarized in the table below for two processes counting together.
Process 1 | SHM Count | Process 2 |
---|---|---|
Lock SHM | 1 | Can't Lock |
if (uneven) wait; else count++ | 1 | Lock SHM |
waiting | 1 >> | if (even) wait; else count++ |
waiting | 2 << | Unlock + Signal |
Done waiting. Received lock | <- 2 | Can't Lock |
Count++ | >> 3 | Can't Lock |
Unlock | 3 | Lock SHM |
Can't Lock | 4 << | if (even) wait; else count++ |
Usage in a feedback loop
Now, how do we use all this? Our goal is to place gazebo as the plant in our feedback loop in simulink and run as fast as possible.
As can be seen, gazebo now serves as the plant.
libshm_ipc
The library for this can be found on the Football Table SVN, called libshm_ipc
. This is basically, thread-safe IPC for beginners, all functions are made so that the shared memory is not accessed unless a lock is achieved. The library functions should be quite self explanatory, however here i'll show a short example off how it is use in in S-function:
int timeout = 2;
IPClient shm_client; //create an IPC client
shm_client.attach(shm_key);
shm_client.lock(); //First we attempt to lock the shared memory
/*Then we wait if needed (giving up lock), we wait if the flag associated with array_index is false*/
int ret = shm_client.flagWait(array_index,false,timeout);
if(ret==ETIMEDOUT){printf("Timed out \n"); ssSetStopRequested(S,1); //If we timed-out we tell simulink to abort the simulation
}else{ //If we are done waiting re-attaining lock, we start to read data (assign it to an output is this case)
for(int i=0; i<8; i++){
y[i] = shm_client.getData(i,0,array_index);//Reading and writing is done using functions usage: getData(row,column,array_index)
}
}
ret = shm_client.flagUpdate(array_index,false);
shm_client.unlock();