How to write a std::queue with a thread which only executes the "newest unexecuted task" from the queue? - queue

I need to create a std::queue of tasks which is executed by a thread which is only executing the latest task from the list.
The tasks are let's say for example file copy tasks. But here is the important part. I don't have to always run through the whole queue of tasks. I just want to execute the latest unexecuted task from the queue and empty the queue right after popping out the task to execute. You see this part can be tricky. I have to be careful to not clear the queue with an unexecuted task pending :-)
Following is the complete code:
#include <atomic>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
using CopyTask = std::function<void(void)>;
class TaskQueue {
public:
~TaskQueue() {
StopThread();
}
void CopyTaskCompleted() {
std::unique_lock<std::mutex> que_lock(m_push_mutex);
m_trigger = true;
que_lock.unlock();
m_condition_variable.notify_one();
std::cout << " CopyTaskCompleted called " << std::endl;
}
void PushCopyTask(CopyTask task) {
std::lock_guard<std::mutex> lock(m_push_mutex);
std::cout << " PushCopyTask called " << std::endl;
if (!m_thread.joinable()) {
StartThreadLooper();
task();
return;
}
if (!m_queue.empty()) {
m_queue.push(task);
} else {
// If the queue is empty then there are no pending tasks. Simply run the task without posting it on the queue
task();
}
}
private:
std::queue<CopyTask> m_queue;
std::mutex m_queue_mutex;
std::mutex m_push_mutex;
std::condition_variable m_condition_variable;
std::thread m_thread;
std::atomic<bool> m_running {false};
bool m_trigger {false};
void StartThreadLooper() {
m_running = true;
std::cout << " Looper thread going to start " << std::endl;
m_thread = std::thread( [this] {
while (m_running) {
std::cout << " Looper thread running " << std::endl;
WaitForNotification();
if (!m_queue.empty()) {
// I want to execute the latest task from the queue here and cancel the rest of the tasks
CopyTask latest_task_to_execute = m_queue.back();
m_queue = {};
latest_task_to_execute();
}
}
});
}
void StopThread() {
std::unique_lock<std::mutex> lock(m_queue_mutex);
m_running = false;
if (m_thread.joinable()) {
try {
m_thread.join();
}
catch (const std::exception& e) {
// Log
}
}
std::cout << " Looper thread exiting " << std::endl;
}
void WaitForNotification() {
std::unique_lock<std::mutex> que_lock(m_queue_mutex);
m_condition_variable.wait(que_lock, [this] {
return m_trigger;
});
m_trigger = false;
}
};
int main() {
std::cout << " -- Beginining of program -- " << std::endl;
TaskQueue task_queue;
task_queue.PushCopyTask([&task_queue](){
std::thread t1([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(70));
std::cout << " PushCopyTask number 1 fninshed executing " << std::endl;
task_queue.CopyTaskCompleted();
});
});
task_queue.PushCopyTask([&task_queue](){
std::thread t2([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(70));
std::cout << " PushCopyTask number 2 fninshed executing " << std::endl;
task_queue.CopyTaskCompleted();
});
});
task_queue.PushCopyTask([&task_queue](){
std::thread t3([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(70));
std::cout << " PushCopyTask number 3 fninshed executing " << std::endl;
task_queue.CopyTaskCompleted();
});
});
task_queue.PushCopyTask([&task_queue](){
std::thread t4([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(70));
std::cout << " PushCopyTask number 4 fninshed executing " << std::endl;
task_queue.CopyTaskCompleted();
});
});
std::cout << " -- Ending of program -- " << std::endl;
return 0;
}
But above does not work. It seems like I am doing something wrong with the condition_variable?
How can write a tasks queue with a simple public API like above class TaskQueue has where I can pass a method to execute and it should always execute the latest method when it gets a chance.
Dev env:
Macos Big Sure with C++ clang compiler

Multiple things here:
You're using a different mutex when setting m_triggered than with the condition variable. You need to use the same one.
You're not securing the acces to m_queue via mutex in the worker thread
PushCopyTask will never push anything to the queue. It takes the mtx, sees that the queue is empty, then executes the task and returns. Simultaneous calls will block while trying to take the mtx.
If only the latest q element is supposed to be executed, why use a queue at all? I suggest using std::optional instead. Then push can just overwrite it.

Related

Pybind11, how to invoke the __repr__ of an object within a std::vector?

I'm binding a type my_type
py::class_<my_type, std::shared_ptr<my_type>>(m, "MyType")
.def("__repr__", [](const my_type& o){return fmt::format("MyType: {}", o);});
as well as a std::vector with
py::bind_vector<std::vector<my_type>>(m, "MyTypeVector");
How can/should I declare MyTypeVector's __repr__ method here if I want its output to be a sequence of MyType.__repr__ for each object in the container?
it is actually very simple. py::bind_vector is just a wrapper around class_ so you can add methods to it just like you would add them to a normal class.
In your case you can just do
py::bind_vector<std::vector<my_type>>(m, "MyTypeVector")
.def("__repr__", [](const std::vector<my_type>& v) {// generate your string here;});
So for making the string representation, I generally define toString methods and the << operator in my c++ classes.
class BadData
{
// lots of stuff going on and removed here
virtual void
putMembers(std::ostream& out) const
{
out << "msg=" << >msg;
out << ", ";
out << "stack=" << stack;
}
virtual std::string
toString() const
{
std::ostringstream out;
out << "BadData(";
putMembers(out);
out << ")";
return out.str();
}
}
inline
std::ostream &operator<<(std::ostream& stream, const BadData &item)
{
stream << item.toString();
return stream;
}
We also have operator<< defined for stl collections
template<class T> inline
std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
{
std::ostringstream out;
out << "Vector[";
if (v.size() > 0) {
for (auto ii = v.cbegin(); ii != v.cend() -1 ; ++ii) {
out << *ii << ", ";
}
out << v.back();
}
out << "]";
os << out.str();
return os;
}
So once you have all those operators defined, your __repr__ method can just look like
.def("__repr__", [](const std::vector<my_type>& v) {
std::stringstream stream;
stream << v;
return stream.str();
})
or in the case of your custom class, like
.def("__repr__", &::my_type::toString)
JesseC helped a lot, but someone pointed out a weakness in that approach: it forces either the classes to define their own operator<<, or the programmer to define it in the bindings (which is a problem if the class has already defined an operator<<, but doesn't match what he or she wants as their __repr__ output). The core library shouldn't need to be aware that it's getting binded and therefore shouldn't be forced to implement such method.
To that end, one can modify the operator<< on the std::vector to:
template<class T>
inline std::string vector_repr(const std::vector<T>& v){
std::ostringstream out;
out << "Vector[";
auto py_vector = py::cast(v);
const auto separator = ", ";
const auto* sep = "";
for( auto obj : py_vector ){
out << sep << obj.attr("__repr__")();
sep = separator;
}
out << "]";
return out.str();
}
along with the binding
py::bind_vector<MyTypeVector>(m, "MyTypeVector")
.def("__repr__", [](const MyTypeVector& v){
return vector_repr(v);
});

microsoft cpprestsdk listen to multiple url with same ip?

I want to use cpprestsdk to make a restful API,
I copied some code from here :
int main()
{
http_listener listener("http://0.0.0.0:9080/demo/work1");
cout<<"start server!"<<endl;
listener.support(methods::GET, handle_get);
listener.support(methods::POST, handle_post);
listener.support(methods::PUT, handle_put);
listener.support(methods::DEL, handle_del);
try
{
listener
.open()
.then([&listener]() {TRACE(L"\nstarting to listen\n"); })
.wait();
while (true);
}
catch (exception const & e)
{
cout << e.what() << endl;
}
return 0;
}
now I have to listen to not only "http://0.0.0.0:9080/demo/work1" , but also "http://0.0.0.0:9080/demo/work2", "http://0.0.0.0:9080/realfunction/work1". All in the same IP and port, but different sub-path
Should I use multiple listener to handle all the url one by one in multi-thread? Or there is any other way to handle this?
You can set
http_listener listener("http://0.0.0.0:9080/");
And then in the handler check the request. In the examples linked in cpprestsdk's github I saw things like
void handle_get(http_request message) {
auto path = uri::split_path(uri::decode(message.relative_uri().path()));
if (path.size() == 2 && path[0] == "demo" && path[1] == "work1") {
// ...
} else if (path.size() == 2 && path[0] == "demo" && path[1] == "work2") {
// ...
} else {
message.reply(status_codes::NotFound);
}
}

boost::bind to class member with null object reference

Take the following example
class foo {
public:
foo() {
cout << "foo has been constructed" << endl;
}
~foo() {};
void DoSomething( int i ) {
cout << "integer = " << i << endl;
}
};
int main() {
auto b = boost::bind( &foo::DoSomething,(foo*)0,_1);
b( 250 );
}
It compiles fine( this doesn't suprise me). But when I call b(), it runs fine. How is this the case? I expected, because I wasn't creating an instance of foo that calling DoSomething would case a run time problem.
Can someone explain where the instance of foo is being created? Since when I run it, I do not see the construction message printed.
foo does not get created at all.
boost-bind takes a pointer to the function. It does not care if it is C style function, a method or class function.
When you give an object instance to boost bind it just uses this object as first parameter. Since you do not require any of the instance members within DoSomething, you do not see any effect of the NULL instance.
Try and modify your example:
class foo {
public:
std:string* test_ptr;
foo() {
test_ptr=new std::string("Test");
cout << "foo has been constructed" << endl;
}
~foo() {};
void DoSomething( int i ) {
cout << "integer = " << i << endl;
cout << this->test_ptr << endl;
this->*test_ptr = "Test2"; // this is really messy with your example
}
static void DoSomething2 ( foo* sameAsThis_ptr, int i) {
this->*test_ptr = "Test3"; // compile error
// same behavior as DoSomething1
cout << "integer = " << i << endl;
cout << sameAsThis_ptr->test_ptr << endl;
sameAsThis_ptr->*test_ptr = "Test4"; // this is really messy with your example
}
};
int main() {
auto b = boost::bind( &foo::DoSomething,(foo*)0,_1);
b( 250 );
auto c = boost::bind ( &foo::DoSomething2, (foo*)NULL; _1);
c( 300 );
}
If you want to learn more you can take a closer look at "function pointers". Boost bind is just a very sophisticated way to make use of function pointers.
It wrap function pointers to an object, making functions to objects ("object functional programming")

pci_disable_msi Oops Bug

I am trying to write a kernel module that will handle MSI interrupts for a PCIe device. I have written a simple skeleton outline for my driver currently and whenever I try to call 'pci_disable_msi(dev)' I get an unable to handle kernel NULL pointer dereference error. I am following along exactly as described from the /Documentation/PCI/MSI-HOWTO.txt and it seems to me that I should not be getting this error. Is this a bug or is my setup incorrect? Judging by the last print that occures, I am pretty sure that it is happening at the fpga_remove() when I call pci_disable_msi(). (Clearly this occures when I am removing the module)
static struct pci_driver fpga_driver = {
.name = "PCIe_test",
.id_table = fpga_dev_table,
.probe = fpga_probe,
.remove = fpga_remove,
.suspend = fpga_suspend,
.resume = fpga_resume,
};
static irqreturn_t fpga_isr(int irq, struct pci_dev *dev)
{
printk(KERN_NOTICE "THIS is the ISR\n");
return IRQ_HANDLED;
}
static int setup_MSI_interrupt(struct pci_dev *dev, int num_msi)
{
int result;
result = pci_enable_msi(dev);
if(result)
{
printk(KERN_WARNING "Could not enable MSI\n");
return result;
}
printk(KERN_NOTICE "MSI has been enabled\n");
printk(KERN_NOTICE "dev->irq line is %d", dev->irq);
result = request_irq(dev->irq, fpga_isr, IRQF_SHARED, fpga_driver.name, dev);
printk(KERN_NOTICE "Using IRQ num %d\n", dev->irq);
if (result) {
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n", dev->irq, result);
goto exit1;
}
dev_info(&dev->dev, "FPGA using PCIe Interrupt\n");
return 0;
exit1:
return -1;
}
static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
printk(KERN_NOTICE "Probing driver\n");
switch(dev->vendor) {
case VENDOR://0x1708:
printk(KERN_NOTICE "Xilinx device found\n");
break;
default:
printk(KERN_NOTICE "Device found that does not match id: id = 0x%04X\n", dev->device);
};
int err = pci_enable_device(dev);
if (err) {
dev_err(&dev->dev, "Failed to enable FPGA PCI device (%d)\n", err);
goto exit;
}
err = setup_MSI_interrupt(dev, NUM_MSI);
if(err)
goto exit;
return 0;
exit:
return -1;
}
static void fpga_remove(struct pci_dev *dev)
{
printk(KERN_NOTICE "REMOVING IRQ # %d\n", dev->irq);
free_irq(dev->irq, dev);
printk(KERN_NOTICE "IRQ has been freed\n");
pci_disable_msi(dev); // This causes a NUll Pointer to be dereferenced but needs to be added
printk(KERN_NOTICE "MSI has been disabled\n");
}
static int __init fpga_init(void)
{
printk(KERN_NOTICE "Registering Driver\n");
return pci_register_driver(&fpga_driver);
return 0;
}
You should probably not use the struct pci_dev as void *dev_id parameter in the request_irq(...) and the free_irq(...) functions. Its important to call them with the right and the same unique dev_id parameter for the interrupt. Doing this not could cause the kernel panic at pci_disable_msi() on removing the module.
Read more here:
What is dev_id parameter in request_irq?

Boost io_service stopping?

I am working on an NPAPI plugin that allows to use sockets with local inside browsers and I am using Boost sockets for this.
My usage right now is just open the socket write a meesage, read, send a closing message and close and then repeat (I know it is stupid to close and open everytime but I can not change that).
The problem is that after the second open I am unable to read from the socket, until las changes I was able to open write but never got the info back and now it seems the io_service thread is just dying.
I have read a lot of tutorial and info, but no one seems to open several client sockets as I am trying to do.
Here are the class that stores the socket info and handler:
SocketInfo.hpp
class SocketInfo
{
public:
void start_read();
void handle_read(const boost::system::error_code& error, std::size_t bytes_transferred);
FB::JSObjectPtr m_callback;
boost::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
char data_[SOCKETS_API_BUFFER];
int key;
boost::shared_ptr<SocketsAPI> parent;
};
SocketInfo.cpp
void SocketInfo::start_read()
{
parent->log("start_read" + boost::lexical_cast<std::string>(key));
m_socket->async_receive(boost::asio::buffer(data_, SOCKETS_API_BUFFER),
boost::bind(&SocketInfo::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void SocketInfo::handle_read(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
if (!error) {
parent->log("handle_read" + boost::lexical_cast<std::string>(key));
std::string str(&data_[0], &data_[0] + bytes_transferred);
m_callback->InvokeAsync("processData", FB::variant_list_of(str));
start_read();
} else {
parent->log("error closing " + boost::lexical_cast<std::string>(key));
m_callback->InvokeAsync("processCancel", FB::variant_list_of());
parent->do_close(*this);
}
}
SocketApi.h
class SocketsAPI : public FB::JSAPIAuto
{
public:
SocketsAPI(const SocketsPtr& plugin, const FB::BrowserHostPtr& host) :
m_plugin(plugin), m_host(host)
{
... FireBreath code here ...
//Start thread with work
workPtr.reset( new boost::asio::io_service::work(io_service));
ioThreadPtr.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)));
}
virtual ~SocketsAPI() {
workPtr.reset();
if (ioThreadPtr) {
ioThreadPtr->join();
}
};
//Socket Methods
int open(const int port, const FB::JSObjectPtr &callback );
void close(const int key);
void write(const int key, const std::string data);
// Method echo
FB::variant echo(const FB::variant& msg);
void do_close(const SocketInfo socket);
void log(const std::string &str);
private:
mapType sockets;
boost::asio::io_service io_service;
boost::shared_ptr<boost::thread> ioThreadPtr;
boost::shared_ptr<boost::asio::io_service::work> workPtr;
void checkOpen(const SocketInfo socket);
void do_write(const std::string data, const SocketInfo socket);
void start_read(const SocketInfo socket);
void empty_handle(const boost::system::error_code& error);
int getFirstEmpty();
SocketInfo getSocket(const int key);
};
SocketAPI.cpp
int SocketsAPI::open(const int port, const FB::JSObjectPtr &callback )
{
log("open");
boost::shared_ptr<SocketInfo> socket;
socket.reset(new SocketInfo);
socket->m_socket.reset(new boost::asio::ip::tcp::socket(io_service));
socket->m_callback = callback;
ip::tcp::endpoint tcp(ip::address::from_string("127.0.0.1"), port);
boost::system::error_code errorcode;
socket->m_socket->connect(tcp, errorcode);
if (errorcode) {
trace("Connection failed: ", errorcode.message());
return -1;
}
log("conenected");
boost::asio::socket_base::keep_alive o(true);
socket->m_socket->set_option(o);
int key = getFirstEmpty();
socket->key = key;
socket->parent.reset(this);
sockets.insert ( std::pair<int,boost::shared_ptr<SocketInfo>>(key,socket));
socket->start_read();
if (io_service.stopped()) {
log("Resetting service");
io_service.reset();
}
return key;
}
void SocketsAPI::close(const int key)
{
SocketInfo socket = getSocket(key);
checkOpen(socket);
log("close");
io_service.post(boost::bind(&SocketsAPI::do_close, this, socket));
}
void SocketsAPI::write(const int key, const std::string data)
{
log("write socket " + boost::lexical_cast<std::string>(key));
SocketInfo socket = getSocket(key);
checkOpen(socket);
io_service.post(boost::bind(&SocketsAPI::do_write, this, Base64::decode(data), socket));
}
void SocketsAPI::checkOpen(const SocketInfo socket)
{
log("checkOpen");
if (!socket.m_socket || !socket.m_socket->is_open()) {
trace("Socket not opened", "");
throw FB::script_error("There is no open socket");
}
}
void SocketsAPI::do_write(const std::string data,
const SocketInfo socket)
{
log("do_write " + boost::lexical_cast<std::string>(socket.key));
if (!socket.m_socket->is_open()) {
return;
}
boost::asio::async_write(*(socket.m_socket.get()),
boost::asio::buffer(&data[0], data.size()),
boost::bind(&SocketsAPI::empty_handle, this, boost::asio::placeholders::error)
);
}
void SocketsAPI::empty_handle(const boost::system::error_code& error)
{
if (error) {
trace("Error writing: ", error.message());
}
}
void SocketsAPI::do_close(const SocketInfo socket)
{
log("do_close");
if (!socket.m_socket || !socket.m_socket->is_open()) {
return;
}
boost::system::error_code errorcode;
socket.m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
if (errorcode) {
trace("Closing failed: ", errorcode.message());
}
socket.m_socket->close(errorcode);
if (errorcode) {
trace("Closing2 failed: ", errorcode.message());
}
mapType::iterator iter = sockets.find(socket.key);
if (iter != sockets.end()) {
sockets.erase (iter);
}
log("do_close end");
}
int SocketsAPI::getFirstEmpty() {
int i = 0;
mapType::iterator iter;
while(true) {
iter = sockets.find(i);
if (iter == sockets.end()) {
return i;
}
i++;
}
}
SocketInfo SocketsAPI::getSocket(const int key) {
mapType::iterator iter = sockets.find(key);
if (iter == sockets.end()) {
trace("Socket not found", "");
throw FB::script_error("Socket not found");
}
log("socket " + boost::lexical_cast<std::string>(key) +" found");
return *iter->second.get();
}
I am sure that something could be improved (please tell me) but I can not find the error why after the second open it just doesn't work.
Traces of excution:
open
conenected
start_read0
write socket 0
socket 0 found
checkOpen
do_write 0
handle_read0
start_read0
write socket 0
socket 0 found
checkOpen
do_write 0
socket 0 found
checkOpen
close
do_close
do_close end
open
conenected
start_read0
write socket 0
socket 0 found
checkOpen
It seems that io_service.run() just stops but the thread is still working and io_service is not stopped so I am not sure what could be happening.
Ok I found the error it was a lot simpler than I thought it just throw an exception and that stop everything but as I was using it inside a browser I didn't notice that.
Still I am unable to solve the problem so you can check: Boost bind object freed on read handler to share some insight.