Integrate the Glib main loop into the libev event loop (C++) - c++

I am trying to integrate the Glib main loop into the libev event loop. Actually, I am using their C++ wrappers: glibmm [1] and ev++ [2]. The main idea was taken from the EV::Glib Perl module [3]. However, my implementation sometimes hangs when I try to perform some async task (e.g., reading a text file from the filesystem):
#include <ev++.h>
#include <giomm.h>
#include <glibmm.h>
struct context;
int to_ev_events(Glib::IOCondition events) {
int result = EV_NONE;
if ((events & Glib::IOCondition::IO_IN) != Glib::IOCondition{})
result |= EV_READ;
if ((events & Glib::IOCondition::IO_OUT) != Glib::IOCondition{})
result |= EV_WRITE;
return result;
}
Glib::IOCondition to_glib_events(int events) {
Glib::IOCondition result{};
if ((events & EV_READ) != EV_NONE) result |= Glib::IOCondition::IO_IN;
if ((events & EV_WRITE) != EV_NONE) result |= Glib::IOCondition::IO_OUT;
return result;
}
struct prepare_watcher {
prepare_watcher(context &c) : ctx{c} {
watcher.priority = EV_MINPRI;
watcher.set(this);
watcher.start();
}
void operator()(ev::prepare &watcher, int revents);
ev::prepare watcher;
context &ctx;
};
struct check_watcher {
check_watcher(context &c) : ctx{c} {
watcher.priority = EV_MAXPRI;
watcher.set(this);
watcher.start();
}
void operator()(ev::check &watcher, int revents);
ev::check watcher;
context &ctx;
};
struct timer_watcher {
timer_watcher() { watcher.priority = EV_MINPRI; }
ev::timer watcher;
};
struct io_watcher {
io_watcher(int fd, int events) {
watcher.priority = EV_MINPRI;
watcher.start(fd, events);
}
ev::io watcher;
};
struct context {
Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::get_default();
ev::default_loop loop;
std::vector<Glib::PollFD> poll_fds;
int priority{};
prepare_watcher prepare{*this};
check_watcher check{*this};
timer_watcher timer;
std::map<int, io_watcher> ios;
};
void prepare_watcher::operator()(ev::prepare &watcher, int revents) {
ctx.context->dispatch();
ctx.context->prepare(ctx.priority);
ctx.poll_fds.clear();
int timeout = 0;
ctx.context->query(ctx.priority, timeout, ctx.poll_fds);
for (Glib::PollFD &poll_fd : ctx.poll_fds) {
int fd = poll_fd.get_fd();
ctx.ios.try_emplace(fd, fd, to_ev_events(poll_fd.get_events()));
}
if (timeout >= 0) ctx.timer.watcher.start(timeout * 1e-3);
}
void check_watcher::operator()(ev::check &watcher, int revents) {
for (Glib::PollFD &poll_fd : ctx.poll_fds) {
int fd = poll_fd.get_fd();
io_watcher &io = ctx.ios.at(fd);
if (io.watcher.is_pending())
poll_fd.set_revents(
to_glib_events(ev_clear_pending(ctx.loop.raw_loop, &io.watcher)));
ctx.ios.erase(fd);
}
if (ctx.timer.watcher.is_active() || ctx.timer.watcher.is_pending())
ctx.timer.watcher.stop();
ctx.context->check(ctx.priority, ctx.poll_fds);
}
int main() {
Gio::init();
context ctx;
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path("file.txt");
file->read_async([&](Glib::RefPtr<Gio::AsyncResult> result) {
file->read_finish(result);
ctx.loop.break_loop(ev::ALL);
});
ctx.loop.run();
}
Any ideas?
[1] https://github.com/GNOME/glibmm
[2] http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#C_API-2
[3] https://github.com/gitpan/EV-Glib/blob/master/Glib.xs

Related

Input/Output Error in FUSE when mknod callback invoked

I am developing sample fuse application.
In my application, Input/Output error has raise when create new file with touch command.
ll_mknod function has invoked when create file with touch command.
How I can fix it?
#define FUSE_USE_VERSION 310
#include <fuse3/fuse_lowlevel.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <regex>
#include <unordered_map>
struct mutex_map {
int counter = 2;
std::mutex _mtx;
std::unordered_map<int, const char*> _data;
std::unordered_map<const char*, int> _rev_data;
public:
int set_value(const char* value) {
std::lock_guard<std::mutex> lock(_mtx);
counter++;
_data[counter] = value;
return counter;
}
const char* get_value(int key) { return _data[key]; }
int get_ino(const char* name) { return _rev_data[name]; }
};
static mutex_map mm;
static int sendmailfs_stat(fuse_ino_t ino, struct stat* stbuf,
size_t name_length) {
stbuf->st_ino = ino;
if (ino == 1) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else {
stbuf->st_mode = S_IFCHR | 0666;
stbuf->st_nlink = 1;
stbuf->st_size = name_length;
}
return 0;
}
static void ll_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
struct fuse_entry_param e;
if (parent != 1) {
fuse_reply_err(req, ENOENT);
return;
}
if (mm._rev_data.find(name) == mm._rev_data.end()) {
fuse_reply_err(req, ENOENT);
return;
}
memset(&e, 0, sizeof(e));
int new_ino = mm.set_value(name);
e.ino = (uint64_t)new_ino;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
sendmailfs_stat(e.ino, &e.attr, strlen(name));
puts("Helloworld2");
printf("strlen:%zu: %s\n", strlen(name), name);
fuse_reply_entry(req, &e);
}
static void ll_mknod(fuse_req_t req, fuse_ino_t parent, const char* name,
mode_t mode, dev_t rdev) {
if (parent != 1) {
fuse_reply_err(req, ENOENT);
return;
}
std::regex re("(\\w+)(\\.|_)?(\\w*)#(\\w+)(\\.(\\w+))+");
if (std::regex_match(name, re)) {
puts("Matched");
int inode_num = mm.set_value(name);
struct fuse_entry_param e;
e.ino = inode_num;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
sendmailfs_stat(e.ino, &e.attr, strlen(name));
fuse_reply_entry(req, &e);
} else {
puts("Unmatched");
fuse_reply_err(req, EINVAL);
}
}
static void ll_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
size_t size, off_t off, struct fuse_file_info* fi) {
if (mm.get_value(ino) == NULL) {
fuse_reply_err(req, EINVAL);
return;
}
fuse_reply_write(req, size);
}
static void ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
if (ino == 1 || !mm.get_value(ino)) {
puts("invalid access due to hello");
fuse_reply_err(req, ENOENT);
} else if ((fi->flags && O_WRONLY)) {
fuse_reply_err(req, EINVAL);
}
fuse_reply_open(req, fi);
}
static void ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr,
int to_set, struct fuse_file_info* fi) {
puts("setattr");
fuse_reply_err(req, EINVAL);
}
static void ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
const char* value, size_t size, int flags) {
puts("setxattr");
fuse_reply_err(req, EINVAL);
}
static void ll_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info* fi) {
puts("getattr");
fuse_reply_err(req, EINVAL);
}
static void ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
size_t size) {
puts("getxattr");
fuse_reply_err(req, EINVAL);
}
static const struct fuse_lowlevel_ops opener = {
.lookup = ll_lookup,
.mknod = ll_mknod,
.open = ll_open,
.write = ll_write,
};
int main(int argc, char* argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session* se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0) {
return 1;
}
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %d\n", fuse_version());
fuse_lowlevel_version();
ret = 0;
goto err_out1;
}
if (opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &opener, sizeof(opener), NULL);
if (fuse_set_signal_handlers(se) != 0) {
goto err_out2;
}
if (fuse_session_mount(se, opts.mountpoint) != 0) {
goto err_out3;
}
fuse_daemonize(opts.foreground);
if (opts.singlethread) {
ret = fuse_session_loop(se);
} else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
ret = fuse_session_loop_mt(se, &config);
}
fuse_session_unmount(se);
err_out3:
fuse_remove_signal_handlers(se);
err_out2:
fuse_session_destroy(se);
err_out1:
free(opts.mountpoint);
fuse_opt_free_args(&args);
return ret ? 1 : 0;
}
I using:
Linux 5.12.15
libfuse 3.10.4

Trying to call a certain function each time

Okay, so I've an assignment with threads.I'm suppose to change the current running threads each period of time, let's say a second. First of all I've created a Thread class:
typedef unsigned long address_t;
#define JB_SP 6
#define JB_PC 7
#define STACK_SIZE (4096)
using namespace std;
class Thread{
public:
enum State{
BLOCKED,
READY,
RUNNING
};
Thread(int tid, void(*f)(void), int stack_size) :
tid(tid), stack_size(stack_size){
address_t sp, pc;
sp = (address_t)stack + STACK_SIZE - sizeof(address_t);
pc = (address_t)f;
sigsetjmp(env, 1);
(env->__jmpbuf)[JB_SP] = translate_address(sp);
(env->__jmpbuf)[JB_PC] = translate_address(pc);
sigemptyset(&env->__saved_mask);
state = READY;
quantums = 0;
}
Thread (){}
address_t translate_address(address_t addr)
{
address_t ret;
asm volatile("xor %%fs:0x30,%0\n"
"rol $0x11,%0\n"
: "=g" (ret)
: "0" (addr));
return ret;
}
State get_state() const
{
return state;
}
void set_state(State state1)
{
state = state1;
}
int get_id() const
{
return tid;
}
pthread_t& get_thread()
{
return thread;
}
sigjmp_buf& get_env()
{
return env;
}
void raise_quantums()
{
quantums ++;
}
int get_quantums()
{
return quantums;
}
int add_to_sync(int tid)
{
sync.push_back(tid);
}
bool appear_in_sync_list(int tid)
{
return (find(sync.begin(), sync.end(), tid) != sync.end());
}
private:
vector<int> sync;
int quantums;
State state;
char stack[STACK_SIZE];
sigjmp_buf env;
pthread_t thread;
int tid;
int stack_size;
};
I've this function which changes threads:
void running_thread(int sigNum)
{
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_SETMASK, &set, NULL);
total_quantum ++;
if (currentThread.get_state() == Thread::RUNNING)
{
Thread& t = ready_threads.back();
ready_threads.pop_back();
currentThread.set_state(Thread::READY);
ready_threads.push_back(currentThread);
sigsetjmp(currentThread.get_env(), 1);
currentThread = t;
t.raise_quantums();
siglongjmp(currentThread.get_env(), 1);
}
if (currentThread.get_state() == Thread::BLOCKED)
{
Thread &t = ready_threads.back();
ready_threads.pop_back();
currentThread.set_state(Thread::BLOCKED);
blocked_threads.push_back(currentThread);
sigsetjmp(currentThread.get_env(), 1);
currentThread = t;
t.raise_quantums();
siglongjmp(currentThread.get_env(), 1);
}
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
It actually doesn't matter what it do, my problem is that it isn't even called.
My program first call this function:
int clock_set()
{
int seconds = quantum / SECOND;
int usecs = quantum - seconds*SECOND;
timer.it_value.tv_sec = seconds;
timer.it_value.tv_usec = usecs;
timer.it_interval.tv_sec = seconds;
timer.it_interval.tv_usec = usecs;
struct sigaction sa;
sa.sa_handler = &running_thread;
if (sigaction(SIGVTALRM, &sa,NULL) < 0) {
cerr << "system error: sigaction error.";
return FAILURE;
}
// Start a virtual timer. It counts down whenever this process is executing.
if (setitimer (ITIMER_VIRTUAL, &timer, NULL)) {
cerr << "system error: setitimer error.";
return FAILURE;
}
return SUCCESS;
}
Basically I was trying to make running_thread get activate each second, so I Was using sigaction and sa_handler.
This is my main function:
int main()
{
uthread_init(1000000) // Initiliaze variable 'quantum' to be a second, this function also calls clock_set
uthread_spawn(&g); // Creating a thread object with function g inserting it to ready_threads vector and to threads vector
uthread_spawn(&f); // creating a thread object with function f inserting it to ready_threads vector and to threads vector
}
The vector "ready_threads" has 2 threads in it.
Why doesn't it call running_thread?

How to asynchronously read/write in C++?

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.

Embedding matplotlib in C++

I am reading a message from a socket with C++ code and am trying to plot it interactively with matplotlib, but it seems Python code will block the main thread, no matter I use show() or ion() and draw(). ion() and draw() won't block in Python.
Any idea how to plot interactively with matplotlib in C++ code?
An example would be really good.
Thanks a lot.
You may also try creating a new thread that does the call to the
blocking function, so that it does not block IO in your main program
loop. Use an array of thread objects and loop through to find an unused
one, create a thread to do the blocking calls, and have another thread
that joins them when they are completed.
This code is a quick slap-together I did to demonstrate what I mean about
using threads to get pseudo asynchronous behavior for blocking functions...
I have not compiled it or combed over it very well, it is simply to show
you how to accomplish this.
#include <pthread.h>
#include <sys/types.h>
#include <string>
#include <memory.h>
#include <malloc.h>
#define MAX_THREADS 256 // Make this as low as possible!
using namespace std;
pthread_t PTHREAD_NULL;
typedef string someTypeOrStruct;
class MyClass
{
typedef struct
{
int id;
MyClass *obj;
someTypeOrStruct input;
} thread_data;
void draw(); //Undefined in this example
bool getInput(someTypeOrStruct *); //Undefined in this example
int AsyncDraw(MyClass * obj, someTypeOrStruct &input);
static void * Joiner(MyClass * obj);
static void * DoDraw(thread_data *arg);
pthread_t thread[MAX_THREADS], JoinThread;
bool threadRunning[MAX_THREADS], StopJoinThread;
bool exitRequested;
public:
void Main();
};
bool MyClass::getInput(someTypeOrStruct *input)
{
}
void MyClass::Main()
{
exitRequested = false;
pthread_create( &JoinThread, NULL, (void *(*)(void *))MyClass::Joiner, this);
while(!exitRequested)
{
someTypeOrStruct tmpinput;
if(getInput(&tmpinput))
AsyncDraw(this, tmpinput);
}
if(JoinThread != PTHREAD_NULL)
{
StopJoinThread = true;
pthread_join(JoinThread, NULL);
}
}
void *MyClass::DoDraw(thread_data *arg)
{
if(arg == NULL) return NULL;
thread_data *data = (thread_data *) arg;
data->obj->threadRunning[data->id] = true;
// -> Do your draw here <- //
free(arg);
data->obj->threadRunning[data->id] = false; // Let the joinThread know we are done with this handle...
}
int MyClass::AsyncDraw(MyClass *obj, someTypeOrStruct &input)
{
int timeout = 10; // Adjust higher to make it try harder...
while(timeout)
{
for(int i = 0; i < MAX_THREADS; i++)
{
if(thread[i] == PTHREAD_NULL)
{
thread_data *data = (thread_data *)malloc(sizeof(thread_data));
if(data)
{
data->id = i;
data->obj = this;
data->input = input;
pthread_create( &(thread[i]), NULL,(void* (*)(void*))MyClass::DoDraw, (void *)&data);
return 1;
}
return 0;
}
}
timeout--;
}
}
void *MyClass::Joiner(MyClass * obj)
{
obj->StopJoinThread = false;
while(!obj->StopJoinThread)
{
for(int i = 0; i < MAX_THREADS; i++)
if(!obj->threadRunning[i] && obj->thread[i] != PTHREAD_NULL)
{
pthread_join(obj->thread[i], NULL);
obj->thread[i] = PTHREAD_NULL;
}
}
}
int main(int argc, char **argv)
{
MyClass base;
base.Main();
return 0;
}
This way you can continue accepting input while the draw is occurring.
~~Fixed so the above code actually compiles, make sure to add -lpthread

linux C++ thread in class

Hi i want to do the class with method which will start in separate thread after creating class. That how i do that:
class Devemu {
int VarInc;
void Increm() {
for(;;) {
if (VarInc > 532) VarInc = 0;
else VarInc++;
}
}
public:
static void* IncWrapper(void* thisPtr) {
((Devemu*) thisPtr)->Increm();
return NULL;
}
Devemu() {
VarInc = 0;
}
int Var() {
return VarInc;
}
};
int main(int argc, char** argv) {
Devemu* em = new Devemu();
pthread_t thread_id;
pthread_create(&thread_id, NULL, &Devemu::IncWrapper, NULL);
for(int i = 0 ;i < 50; i++) {
printf("%d\n", em->Var());
}
return (EXIT_SUCCESS);
}
I unlike that pthread_create in main and IncWrapper method can i change that?
Yes, you can put it in the constructor if you like :
class Devemu {
int VarInc;
pthread_t thread_id;
void Increm() {
for(;;) {
if (VarInc > 532) VarInc = 0;
else VarInc++;
}
}
public:
static void* IncWrapper(void* thisPtr) {
((Devemu*) thisPtr)->Increm();
return NULL;
}
Devemu() {
VarInc = 0;
pthread_create(&thread_id, NULL, &Devemu::IncWrapper, NULL);
}
int Var() {
return VarInc;
}
};
I suppose it's better to put the thread creation in the separate member-function like that:
class Devemu {
...
void run()
{
pthread_create(&thread_id, NULL, &Devemu::IncWrapper, NULL);
}
...
};
Not actually in ctor, because sometimes you (or anyone who uses your code) can forget that thread created in the ctor and start coding like:
Devemu m;
...
Devemu m1;
...
creating unnecessary threads just like instances of the class.
If you want to get working source code you need make next changes:
--- so0.cpp 2019-11-04 11:26:11.101984795 +0000
+++ so1.cpp 2019-11-04 11:26:57.108501816 +0000
## -1,3 +1,7 ##
+#include "stdio.h"
+#include <pthread.h>
+#include <cstdlib>
+
class Devemu {
int VarInc;
## -24,7 +28,7 ##
Devemu* em = new Devemu();
pthread_t thread_id;
-pthread_create(&thread_id, NULL, &Devemu::IncWrapper, NULL);
+pthread_create(&thread_id, NULL, &Devemu::IncWrapper, em);
My variant for resolving your problem is :
#include "stdio.h"
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>
class Devemu {
private:
int VarInc;
pthread_attr_t attr; /* отрибуты потока */
pthread_t thread_id;
void Increm();
public:
static void* IncWrapper (void* thisPtr);
Devemu();
~Devemu();
int Var();
};
void Devemu::Increm() {
while (true) {
if (VarInc > 532) { VarInc = 0; } else { VarInc++; }
}
}
void* Devemu::IncWrapper(void* thisPtr) {
((Devemu*) thisPtr)->Increm();
return NULL;
}
Devemu::~Devemu() {
pthread_cancel (thread_id);
}
Devemu::Devemu() {
VarInc = 0;
/// get default value of arrts
pthread_attr_init(&attr);
/// start thread
pthread_create(&thread_id, &attr, &Devemu::IncWrapper, this);
}
int Devemu::Var() {
return VarInc;
}
int main(int argc, char** argv) {
Devemu* em = new Devemu();
for(int i = 0 ; i < 100; i++) {
printf("%d\n", em->Var());
usleep (10);
}
delete em;
usleep (1000);
return (EXIT_SUCCESS);
}