How can I use appropriate the lock mutex function, for three threads in C++? - c++

I have a question about threads but I think that is difficult to explain, so be patient.
I have two pthreads in a QT/C++ program and one signal, Signal fills a buffer, One thread copies the buffer and one to process the buffer's data.
fill buffer1 ----Copy buffer1 to buffer2----process the buffer's 2 data
Signal's function:
void MainWindow::TcpData()
{
if(socket->bytesAvailable()>(DATA_LEN)) {
QByteArray array = socket ->readAll();
if(pthread_mutex_trylock(&data_mutex)==0)
{
if((p+array.size())<(MAX_TCP_BUFFER_SIZE+100))
{
memcpy(BUFFER+p,array.data(),array.size());
p+=array.size();
}
else {
p=0;
memcpy(BUFFER,array.data(),array.size());
p+=array.size();
}
pthread_mutex_unlock(&data_mutex);
}
}
}
Thread 1:
void *MainWindow::copyTCPdata() {
pthread_mutex_lock(&data_mutex);
while(1) {
if(data_ready) {
pthread_cond_wait(&data_cond,&data_mutex);
continue;
}
/* Move the last part of the previous buffer, that was not processed,
* on the start of the new buffer. */
memcpy(data, data+DATA_LEN, (FULL_LEN-1)*4);
/* Read the new data. */
memcpy(data+(FULL_LEN-1)*4, BUFFER,DATA_LEN);
memcpy(BUFFER,BUFFER+DATA_LEN,p);
if(p>DATA_LEN) p=p-DATA_LEN;
data_ready = 1;
pthread_cond_signal(&data_cond);
pthread_mutex_unlock(&data_mutex);
} }
Thread 2:
void *MainWindow::processData {
while(1) {
if(!data_ready) {
pthread_cond_wait(&data_cond,&data_mutex);
continue;
}
data_ready = 0;
pthread_cond_signal(&data_cond);
pthread_mutex_unlock(&data_mutex);
detectSignal(data);
pthread_mutex_lock(&data_mutex);
}
}
I think am loosing data with this way, but the program is more stable, Can someone suggest me a better solution?

Related

Task Synchronization for Command/Response server (C++/ESP32/FreeRTOS)

I want to synchronize two tasks in a command/response communication server. One task sends data to a serial port and another task receives data on the serial port. Received data should either be returned to the sender task or do something else with it.
I unsuccessfully tried using volatile bool flags but have now found that won't work with C++ (See When to use volatile with multi threading?)
So trying to use semaphores to do it but can't quite figure out how. Some (bad) psuedo-code using volatile bool is below. How/where to modify for semaphore give/take?
Actual code/platform is C++ 11 running on ESP32 (ESP-IDF). Resources are very limited so no C++ std:: libraries.
volatile bool responsePending = false;
volatile bool cmdAccepted = false;
char sharedBuffer[100];
// SENDER //
void Task1()
{
char localBuffer[100];
while (1)
{
responsePending = true;
cmdAccepted = false;
sendMessage();
while (responsePending)
sleep();
strcpy(localBuffer, sharedBuffer);
cmdAccepted = true; // signal Task2
}
}
// RECEIVER //
void Task2()
{
char localBuf[100];
int fd = open();
while (1)
{
if (select())
{
read(fd, localBuf);
if (responsePending)
{
strcpy(sharedBuffer, localBuf);
responsePending = false; // signal Task1
while (!cmdAccepted)
sleep();
}
else
{
// Do something else with the received data
}
}
}
}
Create a queue which holds a struct. One tasks waits for the serial, if it got data it will put the message to the struct and the struct to the queue.
Other task waits for the queue, if there are items in the queue it will process the struct.
Example:
struct queueData{
char messageBuffer[100];
};
QueueHandle_t queueHandle;
void taskOne(){
while(){
// Task one checks if it got serial data.
if( gotSerialMsg() ){
// create a struct
queueData data;
// copy the data to the struct
strcpy( getSerialMSG(), data.messageBuffer );
// send struct to queue ( waits indefinietly )
xQueueSend(queueHandle, &data, portMAX_DELAY);
}
vTaskDelay(1); // Must feed other tasks
}
}
void taskTwo(){
while(){
// Check if a structs has an item
if( uxQueueMessagesWaiting(queueHandle) > 0 ){
// create a holding struct
queueData data;
// Receive the whole struct
if (xQueueReceive(queueHandle, &data, 0) == pdTRUE) {
// Struct holds message like: data.messageBuffer
}
}
vTaskDelay(1); // Must feed other tasks
}
}
The good thing in passing structs to queues is that you can always put more data into it. booleans or ints or any other thing.

How to solve the problem that multithreaded drawing is not smooth?

I wrote a data acquisition program with Qt. I collect data using the child threads of the dual cache region written by QSemphore.
void QThreadShow::run() {
m_stop=false; // when start thread,m_stop=false
int n=fullBufs.available();
if (n>0)
fullBufs.acquire(n);
while (!m_stop) {
fullBufs.acquire(); // wait fo full buffer
QVector<double> dataPackage(BufferSize);
double seq=bufNo;
if (curBuf==1)
for (int i=0;i<BufferSize;i++){
dataPackage[i]=buffer2[i]; // copy data from full buffer
}
else
for (int i=0;i<BufferSize;i++){
dataPackage[i]=buffer1[i];
}
for (int k=0;k<BufferSize;k++) {
vectorQpointFbufferData[k]=QPointF(x,dataPackage[k]);
}
emptyBufs.release(); // release a buffer
QVariant variantBufferData;
variantBufferData.setValue(vectorQpointFbufferData);
emit newValue(variantBufferData,seq); // send data to main thread
}
quit();
}
When a cache of sub-threads has collected 500 data, the data is input into a QVector and sent to the main thread and is directly assigned to a lineseries in qchartview every 20ms for drawing. I use QtChart to chart the data.
void MainWindow::onthreadB_newValue(QVariant bufferData, double bufNo) {
// Analysis of QVariant data
CH1.hardSoftDataPointPackage = bufferData.value<QVector<QPointF>>();
if (ui->CH1_Source->currentIndex()==0) {
for (int p = 0;p<CH1.hardSoftDataPointPackage.size();p++) {
series_CH3->append(CH1.hardSoftDataPointPackage[p]);
}
}
}
There is a timer in the main thread.The interval is 20ms and there is a double time (time = time +1), which controls the X-axis.
void MainWindow::drawAxis(double time) {
// dynamic draw x axis
if (time<100) {
axisX->setRange(0, TimeBase/(1000/FrameRate) * 10);
// FrameRate=50
} else {
axisX->setRange(time-TimeBase/(1000/FrameRate) * 10, time);
}
}
But when I run my program, there is a problem that every time the subthread sends data to the main thread, the main thread gets stuck for a few seconds and the plot also gets stuck for a few seconds. I added a curve in the main thread getting data from the main thread, and found that both two curves will be stuck at the same time. I don't know how to solve this problem.
Besides, I want the main thread to draw the data from the child thread evenly within 20ms, instead of drawing all the points at once.
Your main thread stucks because you copy (add to series) a lot of data at one time. Instead this you can collect all your data inside your thread instance without emitting a signal. And from main thread just take little pieces of collected data every 20 ms.
Something like this:
while(!m_stop)
{
...
//QVariant variantBufferData;
//variantBufferData.setValue(vectorQpointFbufferData);
//emit newValue(variantBufferData,seq);//send data to main thread
//instead this just store in internal buffer
m_mutex.lock();
m_internalBuffer.append(vectorQpointFbufferData);
m_mutex.unlock();
}
Read method
QVector<QPointF> QThreadShow::takeDataPiece()
{
int n = 4;
QVector<QPointF> piece;
piece.reserve(n);
m_mutex.lock();
for (int i = 0; i < n; i++)
{
QPointF point = m_internalBuffer.takeFirst();
piece.append(point);
}
m_mutex.unlock();
return piece;
}
And in Main thread read in timeout slot
void MainWindow::OnDrawTimer()
{
QVector<QPointF> piece = m_childThread.takeDataPiece();
//add to series
...
//drawAxis
...
}

swapcontext failing in many-to-many thread implementation

I am tasked with implementing a many-to-many thread manager in C++. I've got most of it more or less down, but I'm having serious problems with swapcontext in my uthread_yield() method. Here's the code
/*
* uthread.cpp
*
* Created on: Oct 12, 2016
* Author: michael
*/
#include "uthread.h"
#include <semaphore.h>
#include <pthread.h>
#ifndef STDIO_H_
#include <stdio.h>
#endif
namespace std {
/*
* Initializes all the variables and allocates memory when needed
*/
int uthread::maxThreads;
int uthread::currentThreads;
pthread_mutex_t uthread::mapMutex;
pthread_mutex_t uthread::qMutex;
pthread_mutex_t uthread::threadMutex;
map<int,UserThread*>* uthread::threadMap;
priority_queue<UserThread*>* uthread::threadQueue;
void uthread::uthread_init(int numKernelThreads) {
pthread_mutex_t tester;
pthread_mutex_init(&tester,NULL);
uthread::maxThreads=numKernelThreads;
uthread::currentThreads=0;
pthread_mutex_init(&threadMutex,NULL);
pthread_mutex_init(&qMutex,NULL);
pthread_mutex_init(&mapMutex,NULL);
threadQueue= new priority_queue<UserThread*>;
threadMap=new map<int,UserThread*>;
}
int uthread::uthread_create(void (* func)( )) {
//Create ucontext to be used in in
ucontext_t* ucp=(ucontext_t*)malloc(sizeof(ucontext_t));
getcontext(ucp);
ucp->uc_stack.ss_sp=(void*)malloc(16384);
ucp->uc_stack.ss_size=16384;
makecontext(ucp, func, 0); //make the context for a thread running func
//Create UserThread
time_t currTime;
time(&currTime);
UserThread* newThread=new UserThread(ucp,currTime);
//Thread Creation Logic
pthread_mutex_lock(&threadMutex);
if (currentThreads>=maxThreads) {
pthread_mutex_unlock(&threadMutex);
pthread_mutex_lock(&qMutex);
threadQueue->push(newThread);
pthread_mutex_unlock(&qMutex);
return 0;
}
else {
int (*execute)(void *)= (int (*)(void *)) func;
int tid=clone(execute,ucp->uc_stack.ss_sp,CLONE_VM|CLONE_FILES,NULL);
if (tid==-1) { //clone failed
pthread_mutex_unlock(&threadMutex);
return -1;
}
currentThreads++;
pthread_mutex_unlock(&threadMutex);
/*
* Map tid -> UserThread in thread map
*/
threadMap->insert(pair<int,UserThread*>(tid,newThread));
pthread_mutex_unlock(&mapMutex);
return 0;
}
return -1;
}
void uthread::uthread_exit() {
/*
* Get the corresponding UserThread object from the map
*/
printf("Start Exit \n");
int threadID=syscall(SYS_gettid) ;
pthread_mutex_lock(&mapMutex);
if (threadMap->find(threadID)==threadMap->end()) { //Cannot find map;
pthread_mutex_lock(&threadMutex);
currentThreads--;
pthread_mutex_unlock(&threadMutex);
exit(0);
}
printf("Getting Curr Thread\n");
UserThread* currThread= threadMap->at(threadID);
pthread_mutex_unlock(&mapMutex);
pthread_mutex_lock(&qMutex);
if (threadQueue->empty()) { //No items on queue, delete memory references and exit
printf("Queue is Empty");
pthread_mutex_unlock(&qMutex);
pthread_mutex_lock(&mapMutex);
threadMap->erase(threadID);
pthread_mutex_unlock(&mapMutex);
pthread_mutex_lock(&threadMutex);
currentThreads--;
pthread_mutex_unlock(&threadMutex);
delete currThread;
exit(0);
}
else { //Remove and delete memory reference to old thread, set context to new thread
printf("Swapping Queue\n");
UserThread* newThread=threadQueue->top();
threadQueue->pop();
pthread_mutex_unlock(&qMutex);
pthread_mutex_lock(&mapMutex);
threadMap->insert(pair<int,UserThread*>(threadID,newThread)); //Update Map
pthread_mutex_unlock(&mapMutex);
printf("Deleting Current Thread\n");
delete currThread;
printf("Setting Context\n");
setcontext(newThread->ucp);
printf("set context failed\n");
}
}
void uthread::uthread_yield() {
printf("Start Yield \n");
int threadID=syscall(SYS_gettid) ;
pthread_mutex_lock(&mapMutex);
UserThread* currThread= threadMap->at(threadID);
pthread_mutex_unlock(&mapMutex);
pthread_mutex_lock(&qMutex);
if (threadQueue->empty()) {
printf("Queue is empty\n");
pthread_mutex_unlock(&qMutex);
return;
}
else {
printf("Queue Not Empty\n");
currThread->updateRuntime(time(NULL)); //updates run time account for time it's been on thread
UserThread* highestPriority=threadQueue->top();
if (highestPriority->getRunTime()>currThread->getRunTime()) { //highestPriority is lower priority than currently running thread
printf("lowest runtime is running\n");
pthread_mutex_unlock(&qMutex);
return;
}
else {
printf("SwapContext\n");
threadQueue->pop();
threadQueue->push(currThread);
pthread_mutex_unlock(&qMutex);
pthread_mutex_lock(&mapMutex);
threadMap->insert(pair<int,UserThread*>(threadID,highestPriority)); //Update Map reference
pthread_mutex_unlock(&mapMutex);
//Swaps contexts
swapcontext(currThread->ucp,highestPriority->ucp);
printf("Swapcontext Failed\n");
}
}
}
int uthread::startThread(void* arg ) {
printf("Thread Cloned\n");
pthread_mutex_lock(&mapMutex);
int threadID=syscall(SYS_gettid) ;
UserThread* currThread= threadMap->at(threadID);
pthread_mutex_unlock(&mapMutex);
setcontext(currThread->ucp);
return 0;
}
}
And this is the code of my corresponding UserThread object:
/*
* UserThread.cpp
*
* Created on: Oct 12, 2016
* Author: michael
*/
#include "UserThread.h"
/*
* Constructor. UCP is taken in as well as start time
* Run time initialized to 0
*
*/
UserThread::UserThread(ucontext_t *ucp,time_t st) {
this->ucp=ucp;
this->startTime=(time_t*)malloc(sizeof(time_t));
this->runTime=(double*)malloc(sizeof(double));
*startTime=st;
*runTime=0;
}
/**
* Deconstructor
*/
UserThread::~UserThread() {
//free(ucp->uc_stack.ss_sp);
//free(ucp);
free(startTime);
free(runTime);
}
/*
* adds the running time in seconds (as a double) to the current running time. Also updates the start time
*/
void UserThread::updateRuntime(time_t currTime) {
double diffTime=difftime(currTime,*startTime);
*runTime=*runTime+diffTime;
*startTime=currTime;
}
/*
* Just Updates the start time
*/
void UserThread::updateStartTime(time_t newTime) {
*startTime=newTime;
}
/*
* getter
*/
double UserThread::getRunTime() {
double rTime=*runTime;
return rTime;
}
/*
* getter
*/
time_t UserThread::getStartTime() {
return *startTime;
}
/*
* THIS IS REVERSED ON PURPOSE. C++ runs a maximum priority queue by default
* by overloading the < operator backwards, that isn't an issue. Sketchy? Yes
* Also functional
*/
bool UserThread::operator <(UserThread* t2) {
return this->getRunTime() > t2->getRunTime();
}
uthread_yield() will correctly work once for each thread, then fail. Any idea why this is? I've stared at this code for hours and at this point I'm out of ideas.
It's not actually failing: it's just printing your failure message. Your yield implementation finishes with:
swapcontext(currThread->ucp, highestPriority->ucp);
printf("Swapcontext Failed\n");
So the yielding thread swaps away after swapcontext(), which is fine - but when that thread is later swapped back to, it will return from swapcontext() and unconditionally execute the printf(). You need:
if (swapcontext(currThread->ucp, highestPriority->ucp) == -1) {
printf("Swapcontext Failed\n");
/* You need to fix up your queue and map here to account for the
* failure to context switch, and then probably loop back and look
* for another candidate thread to swap to. */
}
I also noticed that your uthread_create() function accesses the map and unlocks mapMutex without locking the mutex first.
You're mixing pthreads functions with the bare clone() syscall, which is unsupported. Use pthread_create() / pthread_exit() for managing the underlying threads. One way to do this is by having thread created by pthread_create() start at a scheduling function that pulls the thread to run from your queue:
void* uthread::newThread(void* arg)
{
pthread_mutex_lock(&qMutex);
if (threadQueue->empty()) {
printf("No thread to start.\n");
return NULL;
}
UserThread* highestPriority = threadQueue->top();
threadQueue->pop();
pthread_mutex_unlock(&qMutex);
int threadID = syscall(SYS_gettid);
pthread_mutex_lock(&mapMutex);
threadMap->insert(pair<int,UserThread*>(threadID,highestPriority)); //Update Map reference
pthread_mutex_unlock(&mapMutex);
setcontext(highestPriority->ucp);
printf("setcontext() Failed\n");
return NULL;
}
...then you can simplify uthread_create() by always pushing the new user thread onto the queue, and only conditionally creating the underlying thread:
// ... start of uthread_create() up to creating new UserThread ...
pthread_mutex_lock(&qMutex);
threadQueue->push(newThread);
pthread_mutex_unlock(&qMutex);
//Thread Creation Logic
pthread_mutex_lock(&threadMutex);
if (currentThreads < maxThreads) {
pthread_t new_pthread;
if (pthread_create(*new_pthread, NULL, uthread::newThread, NULL) != 0) {
printf("New pthread creation failed.\n");
} else {
currentThreads++;
}
}
pthread_mutex_unlock(&threadMutex);
return 0;
By the way, it seems that you're using the threadMap just as a way to implement thread-local-storage: you could instead use the built-in pthreads thread-local-storage API (pthread_key_create() / pthread_setspecific() / pthread_getspecific()).

Figuring out a race condition

I am building a screen recorder, I am using ffmpeg to make the video out from frames I get from Google Chrome. I get green screen in the output video. I think there is a race condition in the threads since I am not allowed to use main thread to do the processing. here how the code look like
This function works each time I get a new frame, I suspect the functions avpicture_fill & vpx_codec_get_cx_data are being rewritten before write_ivf_frame_header & WriteFile are done.
I am thinking of creating a queue where this function push the object pp::VideoFrame then another thread with mutex will dequeue and do the processing below.
What is the best solution for this problem? and what is the optimal way of debugging it
void EncoderInstance::OnGetFrame(int32_t result, pp::VideoFrame frame) {
if (result != PP_OK)
return;
const uint8_t* data = static_cast<const uint8_t*>(frame.GetDataBuffer());
pp::Size size;
frame.GetSize(&size);
uint32_t buffersize = frame.GetDataBufferSize();
if (is_recording_) {
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
// copy the pixels into our "raw input" container.
int bytes_filled = avpicture_fill(&pic_raw, data, AV_PIX_FMT_YUV420P, out_width, out_height);
if(!bytes_filled) {
Logger::Log("Cannot fill the raw input buffer");
return;
}
if(vpx_codec_encode(&codec, &raw, frame_cnt, 1, flags, VPX_DL_REALTIME))
die_codec(&codec, "Failed to encode frame");
while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) {
switch(pkt->kind) {
case VPX_CODEC_CX_FRAME_PKT:
glb_app_thread.message_loop().PostWork(callback_factory_.NewCallback(&EncoderInstance::write_ivf_frame_header, pkt));
glb_app_thread.message_loop().PostWork(callback_factory_.NewCallback(&EncoderInstance::WriteFile, pkt));
break;
default:break;
}
}
frame_cnt++;
}
video_track_.RecycleFrame(frame);
if (need_config_) {
ConfigureTrack();
need_config_ = false;
} else {
video_track_.GetFrame(
callback_factory_.NewCallbackWithOutput(
&EncoderInstance::OnGetFrame));
}
}

QSerialPort - wating for whole data from sender

I'm working with a serial device.
The QSerialPort is in a separate thread.
Thread is created this way:
QThread* serialthread = new QThread;
Serial* serial = new Serial();
serial->moveToThread(serialthread);
When Data is available this signal in my thread worker is emited:
void Serial::process()
{
serialport = new QSerialPort();
connect(this->serialport,SIGNAL(readyRead()),this,SLOT(readyToRead()));
}
void Serial::readyToRead()
{
emit SIG_dataAvailable(this->read());
}
This is the function that reads the data and checks if the data is correct - the second byte on my serial device says how long the rest of the packet is...
QByteArray Serial::read() const
{
QByteArray receivedData;
int length;
receivedData = serialport->readAll();
length = receivedData[1];
if(length != receivedData.length() - 1)
{
qDebug() << "protocol error.";
return NULL;
}
return receivedData;
}
My problem is that the signal QSerialPort::readyRead is emited before the data from the serial device is complete in the buffer. Any idea how to solve this problem?
There is absolutely NO guarantee that you'll get whole data at ONCE. You can solve this problem in some ways.
1) If you have fixed size package you can do something like this:
void foo::onSerialRead()
{
//! Is there whole datagram appears?
if (m_serial->bytesAvailable() < ::package_size) {
//! If not, waiting for other bytes
return;
}
//! Read fixed size datagram.
QByteArray package = m_serial->read(::package_size);
//! And notify about it.
emit packageReady(package);
}
2) If your package size may vary. Then you have to include "hader" in to your package. This header should contain at least "start" byte and data size (Its second byte in your case). And header shuld be fixed size. Then you can do something like this:
void foo::onSerialRead()
{
static QByteArray package;
static bool isHeaderRead = false;
static quint8 startByte = 0;
static quint8 dataSize = 0;
//! Is there whole header appears?
if (m_serial->bytesAvailable() < ::header_size) {
//! If not, waiting for other bytes
return;
}
if (!isHeaderRead) {
//! Read fixed size header.
package.append(m_serial->read(::header_size));
QDataStream out(&package);
out >> startByte;
//! Check is it actually beginning of our package?
if (Q_UNLIKELY(startByte != ::protocol_start_byte)) {
return;
}
out >> dataSize;
isHeaderRead = true;
}
//! Check is there whole package available?
if (Q_LIKELY(dataSize > m_serial->bytesAvailable())) {
//! If not, waiting for other bytes.
return;
}
//! Read rest.
package.append(m_serial->read(dataSize));
//! And notify about it.
emit packageReady(package);
package.clear();
isHeaderRead = false;
}
And there is absolutely no point in putting your QSerial in to different thread.