I am getting this error:
Linking CXX executable muse3.exe
driver/libmuse_driver.a(qttimer.obj):qttimer.cpp:(.rdata$.refptr._ZTVN8MusECore10InnerTimerE[.refptr._ZTVN8MusECore10InnerTimerE]+0x0): undefined reference to `vtable for MusECore::InnerTimer'
collect2.exe: error: ld returned 1 exit status
mingw32-make[2]: *** [muse\CMakeFiles\muse.dir\build.make:212: muse/muse3.exe] Error 1
mingw32-make[1]: *** [CMakeFiles/Makefile2:600: muse/CMakeFiles/muse.dir/all] Error 2
mingw32-make: *** [Makefile:151: all] Error 2
I understand that the message is a little unclear and the issue is probably with some method.
Here is my .h file:
#ifndef __QTTIMER_H__
#define __QTTIMER_H__
#include <fcntl.h>
#include <QThread>
#include <QBasicTimer>
#include <QTimerEvent>
#include "timerdev.h"
namespace MusECore {
//---------------------------------------------------------
// QtTimer
//---------------------------------------------------------
class InnerTimer : public QObject {
Q_OBJECT
int writePipe;
long int tickCount;
QBasicTimer timer;
public:
void setupTimer(int fd, int timeoutms);
~InnerTimer();
long int getTick();
bool isRunning() { return timer.isActive(); }
protected:
void timerEvent(QTimerEvent *event);
};
class QtTimer : public Timer, public QThread {
int writePipe;
int readPipe;
bool keepRunning;
InnerTimer *innerTimer;
int timeoutms;
public:
QtTimer();
virtual ~QtTimer();
virtual signed int initTimer(unsigned long init);
virtual long unsigned int setTimerResolution(unsigned long resolution);
virtual long unsigned int getTimerResolution();
virtual long unsigned int setTimerFreq(unsigned long freq);
virtual long unsigned int getTimerFreq();
virtual bool startTimer();
virtual bool stopTimer();
virtual long unsigned int getTimerTicks(bool printTicks=false);
void setFindBestTimer(bool ) { }
private:
virtual void run();
};
} // namespace MusECore
#endif //__QTTIMER_H__
And here is the .cpp file:
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include "qttimer.h"
#ifndef TIMER_DEBUG
#define TIMER_DEBUG 1
#endif
#ifdef _WIN32
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
#endif
namespace MusECore {
QtTimer::QtTimer()
{
if(TIMER_DEBUG)
fprintf(stderr,"QtTimer::QtTimer(this=%p) called\n",this);
innerTimer = NULL;
timeoutms = 10;
readPipe=-1;
writePipe=-1;
}
QtTimer::~QtTimer()
{
if(TIMER_DEBUG)
fprintf(stderr,"QtTimer::~QtTimer(this=%p) called\n",this);
exit(); // thread exit
}
signed int QtTimer::initTimer(unsigned long)
{
if(TIMER_DEBUG)
printf("QtTimer::initTimer(this=%p)\n",this);
int filedes[2]; // 0 - reading 1 - writing
if (pipe(filedes) == -1) {
perror("QtTimer - creating pipe failed");
exit(-1);
}
#ifndef _WIN32
int rv = fcntl(filedes[1], F_SETFL, O_NONBLOCK);
if (rv == -1)
perror("set pipe O_NONBLOCK");
#endif
if (pipe(filedes) == -1) {
perror("QtTimer - creating pipe1");
exit(-1);
}
writePipe = filedes[1];
readPipe = filedes[0];
return filedes[0];
}
long unsigned int QtTimer::setTimerResolution(unsigned long)
{
return 0;
}
long unsigned int QtTimer::setTimerFreq(unsigned long freq)
{
if (freq > 1000)
freq = 1000;
if (freq < 100)
freq = 100;
timeoutms = 1000/freq;
return 1000/timeoutms;
}
long unsigned int QtTimer::getTimerResolution()
{
return 20;
}
long unsigned int QtTimer::getTimerFreq()
{
return 1000/timeoutms;
}
bool QtTimer::startTimer()
{
QThread::start();
return true;
}
bool QtTimer::stopTimer()
{
QThread::quit();
return true;
}
unsigned long int QtTimer::getTimerTicks(bool /*printTicks*/)
{
if(TIMER_DEBUG)
printf("getTimerTicks()\n");
unsigned long int nn;
if (readPipe==-1) {
fprintf(stderr,"QtTimer::getTimerTicks(): no pipe open to read!\n");
return 0;
}
if (read(readPipe, &nn, sizeof(char)) != sizeof(char)) {
fprintf(stderr,"QtTimer::getTimerTicks(): error reading pipe\n");
return 0;
}
//return nn;
return innerTimer != 0 ? innerTimer->getTick() : 0;
}
void QtTimer::run()
{
//bool keepRunning = true;
innerTimer = new InnerTimer();
innerTimer->setupTimer(writePipe, timeoutms); // make sure it is running in the right thread
exec();
}
void InnerTimer::setupTimer(int fd, int timeoutms)
{
tickCount=0;
writePipe = fd;
timer.start(timeoutms, this);
printf("InnerTimer::setupTimer() started\n");
}
InnerTimer::~InnerTimer()
{
timer.stop();
}
void InnerTimer::timerEvent(QTimerEvent *event)
{
//if (tickCount%1000)
//printf("InnerTimer::timerEvent %ld ++++++++++++++++++++++\n",tickCount);
if (event->timerId() == timer.timerId()) {
tickCount++;
write(writePipe,"t",1);
}
}
long int InnerTimer::getTick()
{
return tickCount;
}
} // namespace MusECore
I have read other posts on this error, and they mostly point to some virtual methods that were not defined. But, to me, it seems that everything is defined properly and I also have the destructor defined.
Can anybody point me out to what is wrong?
Related
I am trying to make a menu for a project of mine. I have it working but when I run a function I want to be able to cancel it but when I run the function I can't get any inputs from my rotary encoder while it is still waiting. I think i need to cancel de function of the rotary encode but I don't now how to do that. How can I read from the rotary encoder while this function is playing?
main.cpp
#include <thread>
#include <iostream>
#include <chrono>
#include <fstream>
#include <vector>
#include <string>
#include <stdio.h>
#include <bcm2835.h>
#include <pigpio.h>
#include "rotary_encoder.hpp"
#include "i2cControl.hpp"
#include "lcdDriver.hpp"
using namespace std;
int pos = 1;
int maxmenu;
int minmenu;
int menustate;
void callback(int way);
void callbackS(int way);
int test(){
while (true){
//wait for conditions or cancel command
}
//run function
}
void homeMenu(){
maxmenu = 2;
minmenu = 1;
menustate = 0;
pos = 1;
//menu
}
void callback(int way){
int prevpos = pos;
pos = pos + way;
if (pos > maxmenu){
pos = minmenu;
}
if (pos < minmenu){
pos = maxmenu;
}
}
void callbackS(int way){
int switchState = way;
//home menu
if (menustate == 0){
if (pos = 1){
menustate == 1;
test();
homeMenu();
}
}
if (menustate == 1){
homeMenu();
}
}
int main(int argc, char *argv[]){
homeMenu();
if (gpioInitialise() < 0) return 1;
re_decoder dec(12, 16, 26, callback, callbackS);
while (true){
}
dec.re_cancel();
gpioTerminate();
return 0;
}
rotary_encoder.cpp
#include <iostream>
#include <thread>
#include <pigpio.h>
#include "rotary_encoder.hpp"
void re_decoder::_pulse(int gpio, int level, uint32_t tick)
{
if (gpio == mygpioA) levA = level; else levB = level;
if (gpio != lastGpio) /* debounce */
{
lastGpio = gpio;
if ((gpio == mygpioA) && (level == 1))
{
if (levB) (mycallback)(1);
}
else if ((gpio == mygpioB) && (level == 1))
{
if (levA) (mycallback)(-1);
}
}
}
void re_decoder::_pulseS(int gpio, int level, uint32_t tick)
{
levS = level;
if (levS) {
(mycallbackS)(2);
}
}
void re_decoder::_pulseEx(int gpio, int level, uint32_t tick, void *user)
{
/*
Need a static callback to link with C.
*/
re_decoder *mySelf = (re_decoder *) user;
mySelf->_pulse(gpio, level, tick); /* Call the instance callback. */
}
void re_decoder::_pulseExS(int gpio, int level, uint32_t tick, void *user)
{
/*
Need a static callback to link with C.
*/
re_decoder *mySelf = (re_decoder *) user;
mySelf->_pulseS(gpio, level, tick); /* Call the instance callback. */
}
re_decoder::re_decoder(int gpioA, int gpioB, int gpioS, re_decoderCB_t callback, re_decoderCB_t callbackS)
{
mygpioA = gpioA;
mygpioB = gpioB;
mygpioS = gpioS;
mycallback = callback;
mycallbackS = callbackS;
levA=0;
levB=0;
levS=0;
lastGpio = -1;
gpioSetMode(gpioA, PI_INPUT);
gpioSetMode(gpioB, PI_INPUT);
gpioSetMode(gpioS, PI_INPUT);
/* pull up is needed as encoder common is grounded */
gpioSetPullUpDown(gpioA, PI_PUD_UP);
gpioSetPullUpDown(gpioB, PI_PUD_UP);
gpioSetPullUpDown(gpioS, PI_PUD_UP);
/* monitor encoder level changes */
gpioSetAlertFuncEx(gpioA, _pulseEx, this);
gpioSetAlertFuncEx(gpioB, _pulseEx, this);
gpioSetAlertFuncEx(gpioS, _pulseExS, this);
}
void re_decoder::re_cancel(void)
{
gpioSetAlertFuncEx(mygpioA, 0, this);
gpioSetAlertFuncEx(mygpioB, 0, this);
gpioSetAlertFuncEx(mygpioS, 0, this);
}
rotary_encoder.hpp
#ifndef ROTARY_ENCODER_HPP
#define ROTARY_ENCODER_HPP
#include <stdint.h>
typedef void (*re_decoderCB_t)(int);
class re_decoder
{
int mygpioA, mygpioB, mygpioS, levA, levB, levS, lastGpio;
re_decoderCB_t mycallback;
re_decoderCB_t mycallbackS;
void _pulse(int gpio, int level, uint32_t tick);
void _pulseS(int gpio, int level, uint32_t tick);
/* Need a static callback to link with C. */
static void _pulseEx(int gpio, int level, uint32_t tick, void *user);
static void _pulseExS(int gpio, int level, uint32_t tick, void *user);
public:
re_decoder(int gpioA, int gpioB, int gpioS, re_decoderCB_t callback, re_decoderCB_t callbackS);
/*
This function establishes a rotary encoder on gpioA and gpioB.
When the encoder is turned the callback function is called.
*/
void re_cancel(void);
/*
This function releases the resources used by the decoder.
*/
};
#endif
I am new at using Google Mock and I encountered an error: "Error: Actual function call count doesn't match EXPECT_CALL(…)".
I am not sure what I am doing incorrectly. I followed this solution suggested here and I am still encountering this error.
Here is the sample code I am working on:
Orig Class
//orig.h
#include <stdio.h>
class Orig {
public:
virtual ~Orig(){}
virtual int this_func(uint8_t x, uint8_t* y, uint8_t z) = 0;
virtual int that_func(uint8_t x, uint8_t* y, uint8_t z) = 0;
virtual void those_func(int abc) = 0;
};
Mock Orig Class
//mock_orig.h
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "calling_class.h"
class MockOrig : public Orig {
public:
MOCK_METHOD3(this_func, int(uint8_t x, uint8_t* y, uint8_t z));
MOCK_METHOD3(that_func, int(uint8_t x, uint8_t* y, uint8_t z));
MOCK_METHOD1(those_func, void(int abc));
};
Calling Class
//calling_class.h
#define MY_CONST1 0x01
#define MY_CONST2 0x02
#define X_CONST1 0x03
#define X_CONST2 0x04
#define SAMPLE_VALUE (20)
#define TOTAL_VALUE (100/SAMPLE_VALUE)
#include "orig.h"
class CallingClass {
public:
explicit CallingClass(Orig * o) : mOrig(o){};
void calling_func(void) {
static int oldValue = 0;
static uint16_t counter = 0;
uint8_t buffer[2] = {0};
int firstBit = 0;
int secondBit = 0;
if (0 == oldValue && (0 == mOrig->this_func(X_CONST1, buffer, 1))
{
secondBit = (int)(buffer[0] & MY_CONST2);
firstBit = (int)(buffer[0] & MY_CONST1);
if((oldValue != secondBit) && (0 != firstBit))
{
mOrig->those_func(secondBit);
oldValue = secondBit;
counter = TOTAL_VALUE;
}
return;
}
if (0 >= --counter && (0 == mOrig->this_func(X_CONST1, buffer, 1)))
{
uint8_t out = buffer[0] | MY_CONST2;
mOrig->that_func(X_CONST2, &out, 1);
oldValue = 0;
}
return;
}
private:
Orig * mOrig;
}
Test Proper
//mock_orig.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <memory>
TEST(myTest, testMyFunction)
{
std::shared_ptr<Orig> mO(new MockOrig);
uint8_t buffer[1];
EXPECT_CALL(*std::static_pointer_cast<MockOrig>(mO), this_func(X_CONST1, buffer, 1))
.Times(1);
CallingClass callerClass(mO.get());
callerClass.calling_func();
}
int main(int argc, char **argv)
{
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
Can somebody please tell me what is still wrong with this? Any help/leads would be greatly appreciated. thanks!
So I was messing around with a ConcurrentQueue (found here) and I am stumbling upon a Seg Fault during the destruction of the ConcurrentQueue (Seg Faults at line 3397 of concurrentqueue.h). I tried using the queue in multiple other contexts however its is only causing a Seg Fault while being used in this context (Code below.) (Which is making me wonder if my code is causing this).
#ifndef MPSCWORKER_MPSCWORKER_HPP
#define MPSCWORKER_MPSCWORKER_HPP
#include <atomic>
#include <condition_variable>
#include <exception>
#include <memory>
#include <thread>
#include "ConcurrentQueue.h"
template <class Type>
class SinkBase;
class SinkNotRegisteredException;
template <class Type, const unsigned int numOfSinks>
class MPSCWorker{
public:
//Funcs
MPSCWorker();
~MPSCWorker(){
Type msg = {};
//Send an empty msg to the reserved channel to say we're done.
send(msg, numOfSinks + 1);
if(workerThread.joinable())
workerThread.join();
for(int i = 0; i <= numOfSinks + 1; i++){
if(sinks[i].get() != nullptr){
sinks[i].get()->onExit();
}
}
}
void start(){
isThreadRunning.store(true);
workerThread = std::thread(run,
std::ref(workQueue),
std::ref(sinks),
std::ref(isThreadRunning),
std::ref(threadCondVar),
std::ref(threadMutex));
}
int addSink(std::unique_ptr<SinkBase<Type>> &&sink, unsigned int sinkID){
if(!isThreadRunning.load()){
if(sinkID <= numOfSinks + 1){
sinks[sinkID] = std::move(sink);
if(!sinks[sinkID].get()->onInit()){
return -3;
}
} else{
return -1;
}
}else{
return -2;
}
return 0;
}
void send(Type msg, unsigned int sinkID){
workQueue.enqueue(InternalType{msg, sinkID});
threadCondVar.notify_one();
}
private:
//Types
struct InternalType{
Type msg;
unsigned int sinkID;
};
//Vars
std::atomic<bool> isThreadRunning;
std::thread workerThread;
std::condition_variable threadCondVar;
std::mutex threadMutex;
//ID = numOfSinks + 1: reserved
std::unique_ptr<SinkBase<Type>> sinks[numOfSinks + 1];
moodycamel::ConcurrentQueue<InternalType> workQueue;
//Funcs
static void run(moodycamel::ConcurrentQueue<InternalType> &workQueue,
std::unique_ptr<SinkBase<Type>> (&sinks)[numOfSinks + 1],
std::atomic<bool> &isThreadRunning,
std::condition_variable &threadCondVar,
std::mutex &threadMutex){
while(isThreadRunning.load()){
InternalType msg = {};
bool isDequeueSuccess = false;
std::unique_lock<std::mutex> lock(threadMutex);
threadCondVar.wait(lock, [&workQueue, &msg, &isDequeueSuccess](){
isDequeueSuccess = workQueue.try_dequeue(msg);
return isDequeueSuccess;
});
if(msg.sinkID == numOfSinks + 1){
isThreadRunning.store(false);
return;
}
if(sinks[msg.sinkID].get() != nullptr){
sinks[msg.sinkID].get()->onProcess(msg.msg);
}else{
throw SinkNotRegisteredException("Sink ID: " + std::to_string(msg.sinkID)
+ " has not been registered. Use addSink"
"to associate this ID with a Sink before"
"starting the worker.");
}
}
}
};
template <class Type, const unsigned int numOfSinks>
MPSCWorker<Type, numOfSinks>::MPSCWorker(){
isThreadRunning.store(false);
for(unsigned int i = 0; i <= numOfSinks +1; i++){
sinks[i] = nullptr;
}
}
class SinkNotRegisteredException : std::exception{
public:
SinkNotRegisteredException(std::string msg){this->msg = msg;}
const char* what(){return msg.c_str();}
std::string whatStr(){return std::string(msg);}
private:
std::string msg;
};
template <class Type>
class SinkBase{
public:
virtual bool onInit() = 0;
virtual void onExit() = 0;
virtual void onProcess(Type data) = 0;
};
#endif //MPSCWORKER_MPSCWORKER_HPP
Here is an example usage of the code above (I know the code is meant for multi-threaded applications but the example is single threaded for simplicity).
#include <iostream>
#include "MPSCWorker.hpp"
template <class Type>
class PrintSink : public SinkBase<Type>{
public:
virtual bool onInit(){return true;}
virtual void onExit(){}
virtual void onProcess(Type data){std::cout << data << std::endl;}
};
int main(){
MPSCWorker<std::string, 1> worker;
std::unique_ptr<PrintSink<std::string>> sink(new PrintSink<std::string>);
if(sink.get() == nullptr){
return -1;
}
worker.addSink(std::move(sink), 0);
worker.start();
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
worker.send("test", 0);
return 0;
}
Now the real question is: Is my code causing the Seg Fault? Or is it ConcurrentQueue that is doing something wrong? And in any case, How can this be solved?
How do you copy one stream to another using dedicated read/write threads in C++?
Let's say I have these methods (not real, but to illustrate the point) to read/write data from. These read/write functions could represent anything (network/file/USB/serial/etc).
// returns the number of bytes read
void read(char* buffer, int bufferSize, int* bytesRead);
// returns the number of bytes written
void write(char* buffer, int bufferSize, int* bytesWritten);
The solution should also be portable.
NOTE: I am aware that Windows has a FILE_FLAG_OVERLAPPED feature, but this assumes that the read/write is file IO. Remember, these read/write methods could represent anything.
Here is the solution I came up with.
Header
#pragma once
#include <stdlib.h>
#include <queue>
#include <mutex>
#include <thread>
#include <chrono>
#include <list>
#include <thread>
#define ASYNC_COPY_READ_WRITE_SUCCESS 0
struct BufferBlock;
struct ReadStream
{
// read a stream to a buffer.
// return non-zero if error occured
virtual int read(char* buffer, int bufferSize, int* bytesRead) = 0;
};
struct WriteStream
{
// write a buffer to a stream.
// return non-zero if error occured
virtual int write(char* buffer, int bufferSize, int* bytesWritten) = 0;
};
class BufferBlockManager
{
public:
BufferBlockManager(int numberOfBlocks, int bufferSize);
~BufferBlockManager();
void enqueueBlockForRead(BufferBlock* block);
void dequeueBlockForRead(BufferBlock** block);
void enqueueBlockForWrite(BufferBlock* block);
void dequeueBlockForWrite(BufferBlock** block);
void resetState();
private:
std::list<BufferBlock*> blocks;
std::queue<BufferBlock*> blocksPendingRead;
std::queue<BufferBlock*> blocksPendingWrite;
std::mutex queueLock;
std::chrono::milliseconds dequeueSleepTime;
};
void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult);
CPP
#include "AsyncReadWrite.h"
struct BufferBlock
{
BufferBlock(int bufferSize) : buffer(NULL)
{
this->bufferSize = bufferSize;
this->buffer = new char[bufferSize];
this->actualSize = 0;
this->isLastBlock = false;
}
~BufferBlock()
{
this->bufferSize = 0;
free(this->buffer);
this->buffer = NULL;
this->actualSize = 0;
}
char* buffer;
int bufferSize;
int actualSize;
bool isLastBlock;
};
BufferBlockManager::BufferBlockManager(int numberOfBlocks, int bufferSize)
{
dequeueSleepTime = std::chrono::milliseconds(100);
for (int x = 0; x < numberOfBlocks; x++)
{
BufferBlock* block = new BufferBlock(bufferSize);
blocks.push_front(block);
blocksPendingRead.push(block);
}
}
BufferBlockManager::~BufferBlockManager()
{
for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
delete (*iterator);
}
}
void BufferBlockManager::enqueueBlockForRead(BufferBlock* block)
{
queueLock.lock();
block->actualSize = 0;
block->isLastBlock = false;
blocksPendingRead.push(block);
queueLock.unlock();
}
void BufferBlockManager::dequeueBlockForRead(BufferBlock** block)
{
WAITFOR:
while (blocksPendingRead.size() == 0)
std::this_thread::sleep_for(dequeueSleepTime);
queueLock.lock();
if (blocksPendingRead.size() == 0)
{
queueLock.unlock();
goto WAITFOR;
}
*block = blocksPendingRead.front();
blocksPendingRead.pop();
queueLock.unlock();
}
void BufferBlockManager::enqueueBlockForWrite(BufferBlock* block)
{
queueLock.lock();
blocksPendingWrite.push(block);
queueLock.unlock();
}
void BufferBlockManager::dequeueBlockForWrite(BufferBlock** block)
{
WAITFOR:
while (blocksPendingWrite.size() == 0)
std::this_thread::sleep_for(dequeueSleepTime);
queueLock.lock();
if (blocksPendingWrite.size() == 0)
{
queueLock.unlock();
goto WAITFOR;
}
*block = blocksPendingWrite.front();
blocksPendingWrite.pop();
queueLock.unlock();
}
void BufferBlockManager::resetState()
{
queueLock.lock();
blocksPendingRead = std::queue<BufferBlock*>();
blocksPendingWrite = std::queue<BufferBlock*>();
for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
(*iterator)->actualSize = 0;
}
queueLock.unlock();
}
struct AsyncCopyContext
{
AsyncCopyContext(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream)
{
this->bufferBlockManager = bufferBlockManager;
this->readStream = readStream;
this->writeStream = writeStream;
this->readResult = ASYNC_COPY_READ_WRITE_SUCCESS;
this->writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
}
BufferBlockManager* bufferBlockManager;
ReadStream* readStream;
WriteStream* writeStream;
int readResult;
int writeResult;
};
void ReadStreamThread(AsyncCopyContext* asyncContext)
{
int bytesRead = 0;
BufferBlock* readBuffer = NULL;
int readResult = ASYNC_COPY_READ_WRITE_SUCCESS;
while (
// as long there hasn't been any write errors
asyncContext->writeResult == ASYNC_COPY_READ_WRITE_SUCCESS
// and we haven't had an error reading yet
&& readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// let's deque a block to read to!
asyncContext->bufferBlockManager->dequeueBlockForRead(&readBuffer);
readResult = asyncContext->readStream->read(readBuffer->buffer, readBuffer->bufferSize, &bytesRead);
readBuffer->actualSize = bytesRead;
readBuffer->isLastBlock = bytesRead == 0;
if (readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// this was a valid read, go ahead and queue it for writing
asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
}
else
{
// an error occured reading
asyncContext->readResult = readResult;
// since an error occured, lets queue an block to write indicatiting we are done and there are no more bytes to read
readBuffer->isLastBlock = true;
readBuffer->actualSize = 0;
asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
}
if (readBuffer->isLastBlock) return;
}
}
void WriteStreamThread(AsyncCopyContext* asyncContext)
{
int bytesWritten = 0;
BufferBlock* writeBuffer = NULL;
int writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
bool isLastWriteBlock = false;
while (
// as long as there are no errors during reading
asyncContext->readResult == ASYNC_COPY_READ_WRITE_SUCCESS
// and we haven't had an error writing yet
&& writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// lets dequeue a block for writing!
asyncContext->bufferBlockManager->dequeueBlockForWrite(&writeBuffer);
isLastWriteBlock = writeBuffer->isLastBlock;
if (writeBuffer->actualSize > 0)
writeResult = asyncContext->writeStream->write(writeBuffer->buffer, writeBuffer->actualSize, &bytesWritten);
if (writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
if (isLastWriteBlock) return;
}
else
{
asyncContext->writeResult = writeResult;
asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
return;
}
}
}
void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult)
{
AsyncCopyContext asyncContext(bufferBlockManager, readStream, writeStream);
std::thread readThread(ReadStreamThread, &asyncContext);
std::thread writeThread(WriteStreamThread, &asyncContext);
readThread.join();
writeThread.join();
*readResult = asyncContext.readResult;
*writeResult = asyncContext.writeResult;
}
Usage
#include <stdio.h>
#include <tchar.h>
#include "AsyncReadWrite.h"
struct ReadTestStream : ReadStream
{
int readCount = 0;
int read(char* buffer, int bufferSize, int* bytesRead)
{
printf("Starting read...\n");
memset(buffer, bufferSize, 0);
if (readCount == 10)
{
*bytesRead = 0;
return 0;
}
// pretend this function takes a while!
std::this_thread::sleep_for(std::chrono::milliseconds(100));
char buff[100];
sprintf_s(buff, "This is read number %d\n", readCount);
strcpy_s(buffer, sizeof(buff), buff);
*bytesRead = strlen(buffer);
readCount++;
printf("Finished read...\n");
return 0;
}
};
struct WriteTestStream : WriteStream
{
int write(char* buffer, int bufferSize, int* bytesWritten)
{
printf("Starting write...\n");
// pretend this function takes a while!
std::this_thread::sleep_for(std::chrono::milliseconds(500));
printf(buffer);
printf("Finished write...\n");
return 0;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
BufferBlockManager bufferBlockManager(5, 4096);
ReadTestStream readStream;
WriteTestStream writeStream;
int readResult = 0;
int writeResult = 0;
printf("Starting copy...\n");
AsyncCopyStream(&bufferBlockManager, &readStream, &writeStream, &readResult, &writeResult);
printf("Finished copy... readResult=%d writeResult=%d \n", readResult, writeResult);
getchar();
return 0;
}
EDIT: I put my solution into a GitHub repository here. If you wish to use this code, refer to the repository since it may be more updated than this answer.
Typically, you would just have one thread for each direction that alternates between reads and writes.
I have a class server for which I have created a signal joined(QString name). I call it in a function called join(QString name), however I'm getting the error
Server.o: In function Server::join(QString)':
Server.cpp:(.text+0x48): undefined reference to
Server::joined(QString)' collect2: ld returned 1 exit status
This is what my header file looks like:
#ifndef SERVER_H
#define SERVER_H
#include <QString>
#include <mqueue.h>
#include <QVector>
#include <QStringList>
#include "../src/messages.h"
class Server
{
public:
Server();
void start();
private:
void join(QString name);
char buf[MSG_SIZE], msgSend[MSG_SIZE];
QVector<mqd_t> mq_external;
QVector<QString> users;
mqd_t mq_central;
struct mq_attr attr;
signals:
void joined(QString name);
};
#endif // SERVER_H
and this is my cpp file:
#include "Server.h"
using namespace std;
Server::Server()
{
}
void Server::start(){
attr.mq_maxmsg = 100;
attr.mq_msgsize = MSG_SIZE;
attr.mq_flags = 0;
mq_unlink(CENTRALBOX);
mq_central = mq_open(CENTRALBOX, O_RDONLY | O_CREAT, S_IRWXU, &attr);
while(1)
{
int tempMsgVal = mq_receive(mq_central, buf, MSG_SIZE, 0);
if(tempMsgVal != -1){
QString tempS = buf;
QStringList tempSL = tempS.split(":");
if(tempSL.size() == 2 && tempSL.at(0) == "started")
{
int x = 0;
bool exists = false;
for(int i = 0; i < mq_external.size(); i++)
{
x = QString::compare(tempSL[1], users.at(i), Qt::CaseInsensitive);
if(x == 0)
{
exists = true;
break;
}
}
if(!exists)
{
sprintf(buf,"joined");
QString tempS1 = tempSL[1] + "new";
QByteArray byteArray = tempS1.toUtf8();
const char* tempr = byteArray.constData();
mqd_t tempMQ = mq_open(tempr, O_RDWR);
int tempI = mq_send(tempMQ, buf, strlen(buf), 0);
}
else
{
sprintf(buf,"invalidname");
QString tempS1 = tempSL[1] + "new";
QByteArray byteArray = tempS1.toUtf8();
const char* tempr = byteArray.constData();
mqd_t tempMQ = mq_open(tempr, O_RDWR);
int tempI = mq_send(tempMQ, buf, strlen(buf), 0);
}//Endelse
}//Endif
}//Endif
}//Endwhile
}
void Server::join(QString name)
{
emit joined(name);
}
At the beginning of your class declaration you should have the macro Q_OBJECT and be sure to inherit from some QObject descendant.
Apart from whatever is mentioned in this answer, also make sure to:
click 'Build' option > Run 'qmake'
That should fix the pending errors as well.