I use ftd3xx.dll to communicate with the device
The data read part and the data write part are divided into threads and used.
#include <thread>
#include <queue>
#include <array>
#include <windows.h>
using namespace std;
bool dataRead = false;
CRITICAL_SECTION sec;
queue< vector<unsigned short>> BufferQueue;
unsigned WINAPI Write(void* arg) {
int Width = 1000;
vector<unsigned short> data;
data.reserve(Width);
while (Opened)
{
while (dataRead)
{
if (BufferQueue.size() > 0) {
EnterCriticalSection(&sec);
data = BufferQueue.front();
BufferQueue.pop();
LeaveCriticalSection(&sec);
}
else
{
this_thread::sleep_for(2ms);
continue;
}
//wrtie something
}
if (!dataRead)
break;
}
_endthreadex(0);
return 0;
}
unsigned WINAPI Read(void* arg) {
int Width = 1000;
vector<unsigned short> data(Width);
BYTE* acReadBuf = new BYTE[Width];
ULONG ulBytesRead = 0;
int idx = 0;
Sleep(100);
while (dataRead)
{
ftStatus = FT_ReadPipe(ftHandle, CstReadPipeNo, acReadBuf, Width, &ulBytesRead, NULL);
if (FT_SUCCESS(ftStatus))
{
idx = 0;
for (int i = 0; i < Width; i++) {
data[i] = ((unsigned short)((unsigned short)acReadBuf[idx] | ((unsigned short)acReadBuf[idx + 1] << 8)));
idx += 2;
}
EnterCriticalSection(&sec);
if (BufferQueue.size() > 10000) {
queue< vector<unsigned short>> empty;
swap(BufferQueue, empty);
}
BufferQueue.push(data);
LeaveCriticalSection(&sec);
}
else
{
}
}
_endthreadex(0);
return 0;
}
void main() {
//start
InitializeCriticalSection(&sec);
dataRead = true;
HANDLE r_hThread = NULL;
unsigned r_threadID;
r_hThread = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, &r_threadID);
HANDLE w_hThread = NULL;
unsigned w_threadID;
w_hThread = (HANDLE)_beginthreadex(NULL, 0, Write, NULL, 0, &w_threadID);
//....///
//stop
dataRead = false;;
WaitForSingleObject(r_hThread, INFINITE);
WaitForSingleObject(w_hThread, INFINITE);
DeleteCriticalSection(&sec);
}
I want to queue the array directly, but first I am using it as a vector.
Importantly, data loss occurs when other programs are run or even calculators are run.
The same is true even if the device gives the data late or fast.
I would be grateful if someone could help me.
Related
I want to setup an asynchronous bulk transfer to a callback routine but I never enter the callback routine. I changed the code to a synchronous transfer and it works. Please help me understand what I'm doing wrong with the asynchronous transfer. Below is the synchronous transfer
#include <stdio.h>
#include <libusb.h>
#define LB04_VID 4302
#define LB04_PID 60307
unsigned char in_buf[32];
void hexdump(unsigned char* data, int len)
{
int i;
for (i = 0; i < len; i++) printf("%02X ", data[i]);
puts("\n");
}
int main(int argc, char* argv[])
{
libusb_device** devs;
libusb_device_handle* dev_handle;
libusb_context* context = NULL;
size_t list;
int ret;
int iLen;
ret = libusb_init(&context);
if (ret < 0) {
perror("libusb_init");
return 1;
}
libusb_set_option(context, LIBUSB_OPTION_MAX);
list = libusb_get_device_list(context, &devs);
if (list < 0) {
perror("libusb_get_device_list");
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(context, LB04_VID, LB04_PID);
libusb_free_device_list(devs, 1);
printf("found XHC-HB04 device\n");
if (dev_handle) {
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}
ret = libusb_claim_interface(dev_handle, 0);
if (ret < 0) {
perror("libusb_claim_interface");
return 1;
}
ret = libusb_set_configuration(dev_handle, 1);
while (1) {
ret = libusb_bulk_transfer(dev_handle, (0x01 | LIBUSB_ENDPOINT_IN), in_buf, sizeof(in_buf), &iLen, 0);
hexdump((unsigned char*)&in_buf, iLen);
}
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
}
libusb_exit(context);
}
This loops forever like I want. However, when I try to use asynchronous transfer like the below, my callback never gets called. Please help as I'm stuck with what to try next.
#include <stdio.h>
#include <libusb.h>
#define LB04_VID 4302
#define LB04_PID 60307
unsigned char in_buf[32];
libusb_device** devs;
libusb_device_handle* dev_handle;
libusb_context* context = NULL;
struct libusb_transfer* transfer_in = NULL;
int setup_asynch_transfer(libusb_device_handle* dev_handle);
void hexdump(unsigned char* data, int len)
{
int i;
for (i = 0; i < len; i++)
printf("%02X ", data[i]);
puts("\n");
}
void cb_response_in(struct libusb_transfer* transfer)
{
printf("cb_response_in\n");
if (transfer->actual_length > 0)
hexdump((unsigned char*)&in_buf, transfer->actual_length);
setup_asynch_transfer(transfer->dev_handle);
}
int setup_asynch_transfer(libusb_device_handle* dev_handle)
{
int ret;
printf("setup_asynch_transfer\n");
transfer_in = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer_in, dev_handle, (0x01 | LIBUSB_ENDPOINT_IN),
in_buf, sizeof(in_buf),
cb_response_in, NULL, 500); // no user data
return (ret = libusb_submit_transfer(transfer_in));
}
int main(int argc, char* argv[])
{
size_t list;
int ret;
ret = libusb_init(&context);
if (ret < 0) {
perror("libusb_init");
return 1;
}
libusb_set_option(context, LIBUSB_OPTION_MAX);
list = libusb_get_device_list(context, &devs);
if (list < 0) {
perror("libusb_get_device_list");
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(context, LB04_VID, LB04_PID);
libusb_free_device_list(devs, 1);
printf("found XHC-HB04 device\n");
if (dev_handle) {
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}
ret = libusb_claim_interface(dev_handle, 0);
if (ret < 0) {
perror("libusb_claim_interface");
return 1;
}
ret = libusb_set_configuration(dev_handle, 1);
ret = setup_asynch_transfer(dev_handle);
// Loop forever
while (1);
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
}
libusb_exit(context);
}
I made sure I could execute a bulk synchronous transfer and then converted the code to execute asynchronous transfer. I expected the callback routine to print the data and schedule another asynchronous routine while the main routine waited in a while loop forever.
I'm using libusb-1.0 and Microsoft Visual Studio Community 2022 on Windows 10.
#include <iostream>
#include <queue>
#include <thread>
#include <process.h>
#include <windows.h>
#include <stdlib.h>
int g_Width = 100;
std::queue<std::vector<unsigned short>> BufferQueue;
bool dataRead = false;
unsigned short* g_pImgBuf = NULL;
unsigned int _stdcall Write(void* arg) {
std::vector<unsigned short> data;
data.reserve(g_Width);
int line = 0;
while (dataRead)
{
if (!dataRead)
break;
for (int i = 0; i < g_Width; i++) {
data.push_back(i);
}
BufferQueue.push(data);
data.clear();
}
_endthreadex(0);
return 0;
}
unsigned int _stdcall Read(void* arg) {
std::vector<unsigned short> data;
data.reserve(g_Width);
unsigned short color = 0;
int line = 0;
while (dataRead)
{
g_pImgBuf = new unsigned short[g_Width];
if (!BufferQueue.empty()) {
data = BufferQueue.front();
BufferQueue.pop();
}
else if (!dataRead)
break;
else {
continue;
}
for (int j = 0; j < g_Width; j++) {
color = data[j];
color += 2;
g_pImgBuf[j] = color;
}
data.clear();
}
if (g_pImgBuf) { free(g_pImgBuf); g_pImgBuf = NULL; }
_endthreadex(0);
return 0;
}
int main()
{
dataRead = true;
HANDLE r_hThread = NULL;
unsigned r_threadID;
r_hThread = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, &r_threadID);
HANDLE w_hThread = NULL;
unsigned w_threadID;
w_hThread = (HANDLE)_beginthreadex(NULL, 0, Write, NULL, 0, &w_threadID);
while (true)
{
Sleep(100);
}
}
The following error occurred
vector subscript out of range 1501 error
In the middle of the thread function
Give it sleep (2) and it works sometimes but soon I get an error.
If the vector is the problem, is there any other way to store the array in the queue?
I don't know where the problem is
Is there a good way?
You simply cannot do this in such a simplistic way. If you want one thread to pick messages off the queue that are being written by another thread you need mtuexes and condition variables. This is not a trivial task. I suggest a lot of googling. I will look too and updat here if I find a good link
https://juanchopanzacpp.wordpress.com/2013/02/26/concurrent-queue-c11/
https://gist.github.com/ictlyh/f8473ad0cb1008c6b32c41f3dea98ef5
I wanted to write simple multithread app in C/C++. Function funProducent produces 100 values and if random generated value is in given range, char is added to buffer. Function funKonzument comsumes values from buffer. Here is my code:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#define BUFFER_LIMIT 20
struct struktura{
pthread_mutex_t mutex;
pthread_cond_t bufferNotFull;
pthread_cond_t bufferNotEmpty;
int bufferIndex;
char * buffer;
int junk;
};
void * funProducent(void *arg){
struktura * data = (struktura *) arg;
int i = 0;
while (i < 100) {
pthread_mutex_lock(&data->mutex);
if(data->bufferIndex == BUFFER_LIMIT - 1){
pthread_cond_wait(&data->bufferNotFull, &data->mutex);
}
int randomValue = (rand() % 20) + 1;
if( randomValue < 13 ){
data->buffer[++data->bufferIndex] = 'a';
printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
pthread_cond_signal(&data->bufferNotEmpty);
} else {
data->junk++;
}
pthread_mutex_unlock(&data->mutex);
i++;
}
printf("producent is done\n");
}
void * funKonzument(void *arg){
struktura * data = (struktura *) arg;
int i = 0;
while (i + data->junk < 100) {
printf("%d\n", i + data->junk);
pthread_mutex_lock(&data->mutex);
if(data->bufferIndex < 0){
pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);
}
printf("%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
data->bufferIndex--;
pthread_cond_signal(&data->bufferNotFull);
pthread_mutex_unlock(&data->mutex);
i++;
}
printf("konzument is done\n");
}
int main(int argc, char** argv) {
pthread_t threadProducent, threadKonzument;
struktura threadData;
threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
threadData.bufferIndex = -1;
threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
threadData.mutex = PTHREAD_MUTEX_INITIALIZER;
threadData.junk = 0;
pthread_create(&threadProducent, NULL, funProducent, &threadData);
pthread_create(&threadKonzument, NULL, funKonzument, &threadData);
pthread_join(threadProducent, NULL);
pthread_join(threadKonzument, NULL);
free(threadData.buffer);
pthread_mutex_destroy(&threadData.mutex);
pthread_cond_destroy(&threadData.bufferNotFull);
pthread_cond_destroy(&threadData.bufferNotEmpty);
return 0;
}
When I try to run this code, sometimes it stucks in funKonzument at this line:
pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);
But...when I change condition in funProducent method from:
if( randomValue < 13 )
to
if( randomValue > 8 )
everything works fine. Is anyone able to explain me what magic difference is between this two conditions?
You are probably suffering from spurious wakes and some problem with the junk counter. I just removed that counter and added a cond wait loop function (and a little lock context manager) and then the hangings seems to have stopped.
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdexcept>
#include <functional>
#define BUFFER_LIMIT 20
struct struktura{
pthread_mutex_t mutex;
pthread_cond_t bufferNotFull;
pthread_cond_t bufferNotEmpty;
int bufferIndex;
char * buffer;
};
// a lock context manager
class mlock {
pthread_mutex_t* mtx;
public:
mlock(pthread_mutex_t& Mtx) :
mtx(&Mtx)
{
int rv=pthread_mutex_lock(mtx);
if(rv) throw std::runtime_error(std::to_string(rv));
}
mlock(const mlock&) = delete;
mlock(mlock&&) = delete;
mlock& operator=(const mlock&) = delete;
mlock& operator=(mlock&&) = delete;
~mlock() {
pthread_mutex_unlock(mtx);
}
};
// silly loop to take care of spurious wakes
void cwait(pthread_cond_t& c, pthread_mutex_t& m, std::function<bool()> f) {
while(f()) pthread_cond_wait(&c, &m);
}
void* funProducent(void *arg){
struktura* data = static_cast<struktura*>(arg);
int i = 0;
while(i < 100) {
mlock dummy(data->mutex);
cwait(data->bufferNotFull, data->mutex, [&](){return data->bufferIndex == BUFFER_LIMIT - 1;});
int randomValue = (rand() % 20) + 1;
if( randomValue < 13 ){
data->buffer[++data->bufferIndex] = 'a';
printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
i++;
pthread_cond_signal(&data->bufferNotEmpty);
}
}
printf("producent is done\n");
return nullptr;
}
void* funKonzument(void *arg){
struktura* data = static_cast<struktura*>(arg);
int i = 0;
while(i < 100) {
mlock dummy(data->mutex);
cwait(data->bufferNotEmpty, data->mutex, [&](){return data->bufferIndex<0;});
printf("\t\t\t%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
data->bufferIndex--;
i++;
pthread_cond_signal(&data->bufferNotFull);
}
printf("\t\t\tkonzument is done\n");
return nullptr;
}
int main() {
pthread_t threadProducent, threadKonzument;
struktura threadData;
threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
threadData.bufferIndex = -1;
threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
threadData.mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_create(&threadProducent, NULL, funProducent, &threadData);
pthread_create(&threadKonzument, NULL, funKonzument, &threadData);
pthread_join(threadProducent, NULL);
pthread_join(threadKonzument, NULL);
free(threadData.buffer);
pthread_mutex_destroy(&threadData.mutex);
pthread_cond_destroy(&threadData.bufferNotFull);
pthread_cond_destroy(&threadData.bufferNotEmpty);
return 0;
}
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.
// windows_procon.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <windows.h>
#include <process.h>
using namespace std;
HANDLE mutex;
HANDLE emptySlots;
HANDLE filledSlots;
#define BUFFER_SIZE 10
void *producer(void *);
void *consumer(void *);
int produceItem(void);
void printBuffer(void);
int buffer[BUFFER_SIZE];
int head = 0;
int tail = 0;
int _tmain(int argc, _TCHAR* argv[])
{
DWORD prodThrdID, consThrdID;
mutex = CreateMutex(NULL,FALSE,NULL);
emptySlots = CreateSemaphore(NULL,BUFFER_SIZE,BUFFER_SIZE,NULL);
filledSlots = CreateSemaphore(NULL,0,0,NULL);
srand(time(NULL));
_beginthreadex(NULL, 0, (unsigned int(__stdcall *)(void*))producer,
0, 0, (unsigned int *)&prodThrdID);
_beginthreadex(NULL, 0, (unsigned int(__stdcall *)(void*))consumer,
0, 0, (unsigned int *)&consThrdID);
return 0;
}
void *producer(void *n)
{
int item;
for(int i = 0; i <BUFFER_SIZE+5; i++)
{
WaitForSingleObject(emptySlots,INFINITE);
WaitForSingleObject(mutex,INFINITE);
item = produceItem();
//printf("Producing");
cout << "Producing: " << item << endl;
//logfile << "Producing: "<< item << endl;
//fprintf(logfile, "Producing: %d \n", item);
buffer[head] = item;
head = (head + 1) % BUFFER_SIZE;
printBuffer();
ReleaseMutex(mutex);
ReleaseSemaphore(filledSlots,1, NULL);
}
return n;
}
void *consumer(void *n)
{
for(int i = 0; i <BUFFER_SIZE+5; i++)
{
WaitForSingleObject(filledSlots,INFINITE);
//Sleep(500);
WaitForSingleObject(mutex,INFINITE);
cout << "Consuming: " << buffer[tail] << endl;
buffer[tail] = 0;
tail = (tail + 1) % BUFFER_SIZE;
printBuffer();
ReleaseMutex(mutex);
ReleaseSemaphore(emptySlots,1, NULL);
}
return n;
}
int produceItem(void)
{
int x = (rand()%11 + 1);
return x;
}
void printBuffer(void)
{
for(int i = 0; i <BUFFER_SIZE; i++)
{
printf("%d ", buffer[i]);
}
printf("END \n");
}
My program here is supposed to be an algorithm for the producer-consumer problem. I think I have the algorithm correct my only problem is that I'm having trouble getting the threads to run properly. Can someone tell me what the issue is?
You need to wait for the threads you create with _beginthreadex to do their work, as it stands you program will exit immediately after creating them. I haven't looked any further at you logic.
Here is an example.
hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID );
WaitForSingleObject( hThread, INFINITE );