[asio] reopening of serial port fails - why?

Hi! The code below perfectly reads characters from the serial line for 6 seconds. The termination of the thread and a reopening of the connection then fails. What am i missing here? Markus ---snip--- // TestBoostAsio.cpp : Defines the entry point for the console application. // #include "stdafx.h" //int _tmain(int argc, _TCHAR* argv[]) //{ // return 0; //} /* minicom.cpp A simple demonstration minicom client with Boost asio Parameters: baud rate serial port (eg /dev/ttyS0 or COM1) To end the application, send Ctrl-C on standard input */ #include <deque> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/asio/serial_port.hpp> #include <boost/thread.hpp> #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <iostream> #ifdef POSIX #include <termios.h> #endif using namespace std; class minicom_client { public: minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device) : active_(true), io_service_(io_service), serialPort(io_service, device) { if (!serialPort.is_open()) { cerr << "Failed to open serial port\n"; return; } boost::asio::serial_port_base::baud_rate baud_option(baud); serialPort.set_option(baud_option); // set the baud rate after the port has been opened read_start(); } void write(const char msg) // pass the write data to the do_write function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_write, this, msg)); } void close() // call the do_close function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code())); } bool active() // return true if the socket is still active { return active_; } private: static const int max_read_length = 512; // maximum amount of data to read in one operation void read_start(void) { // Start an asynchronous read and call read_complete when it completes or fails serialPort.async_read_some(boost::asio::buffer(read_msg_, max_read_length), boost::bind (&minicom_client::read_complete, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void read_complete(const boost::system::error_code& error, size_t bytes_transferred) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // read completed, so process the data cout.write(read_msg_, bytes_transferred); // echo to standard output read_start(); // start waiting for another asynchronous read again } else do_close(error); } void do_write(const char msg) { // callback to handle write call from outside this class bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written? write_msgs_.push_back(msg); // store in write buffer if (!write_in_progress) // if nothing is currently being written, then start write_start(); } void write_start(void) { // Start an asynchronous write and call write_complete when it completes or fails boost::asio::async_write(serialPort, boost::asio::buffer(&write_msgs_.front(), 1), boost::bind (&minicom_client::write_complete, this, boost::asio::placeholders::error)); } void write_complete(const boost::system::error_code& error) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // write completed, so send next write data write_msgs_.pop_front(); // remove the completed data if (!write_msgs_.empty()) // if there is anthing left to be written write_start(); // then start sending the next item in the buffer } else do_close(error); } void do_close(const boost::system::error_code& error) { // something has gone wrong, so close the socket & make this object inactive if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel() return; // ignore it because the connection cancelled the timer if (error) cerr << "Error: " << error.message() << endl; // show the error message else cout << "Error: Connection did not succeed.\n"; cout << "Press Enter to exit\n"; serialPort.close(); active_ = false; } private: bool active_; // remains true while this object is still operating boost::asio::io_service& io_service_; // the main IO service that runs this connection boost::asio::serial_port serialPort; // the serial port this instance is connected to char read_msg_[max_read_length]; // data read from the socket deque<char> write_msgs_; // buffered write data }; int main(int argc, char* argv[]) { // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress // On other systems, you'll need to look for an equivalent #ifdef POSIX termios stored_settings; tcgetattr(0, &stored_settings); termios new_settings = stored_settings; new_settings.c_lflag &= (~ICANON); new_settings.c_lflag &= (~ISIG); // don't automatically handle control- C tcsetattr(0, TCSANOW, &new_settings); #endif try { if (argc != 3) { cerr << "Usage: minicom <baud> <device>\n"; return 1; } boost::asio::io_service io_service; // define an instance of the main class of this program minicom_client c(io_service, boost::lexical_cast<unsigned int> (argv[1]), argv[2]); // run the IO service as a separate thread, so the main thread can block on standard input while (true) // PROBLEM: reentering this loop fails - why? { boost::thread t(boost::bind (&boost::asio::io_service::run, &io_service)); boost::asio::io_service io; boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3)); timer.wait(); c.write('a'); boost::asio::deadline_timer timer2(io, boost::posix_time::seconds(3)); timer2.wait(); //while (c.active()) // check the internal state of the connection to make sure it's still running //{ // char ch; // cin.get(ch); // blocking wait for standard input // if (ch == 32) // ctrl-C to end program // break; // c.write(ch); //} c.close(); // close the minicom client connection t.join(); // wait for the IO service thread to close std::cout << "-------------------" << std::endl; } } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } #ifdef POSIX // restore default buffering of standard input tcsetattr(0, TCSANOW, &stored_settings); #endif return 0; }

Markus Werle wrote:
Hi!
The code below perfectly reads characters from the serial line for 6 seconds. The termination of the thread and a reopening of the connection then fails.
What am i missing here?
Markus
... snip ...
boost::asio::io_service io_service; // define an instance of the main class of this program minicom_client c(io_service, boost::lexical_cast<unsigned int> (argv[1]), argv[2]); // run the IO service as a separate thread, so the main thread can block on standard input
while (true) // PROBLEM: reentering this loop fails - why? {
You need to call reset() on the io_service instance before you can call run() for a second time.
boost::thread t(boost::bind (&boost::asio::io_service::run, &io_service));
... snip ... HTH Bill Somerville

Bill Somerville <bill <at> classdesign.com> writes:
You need to call reset() on the io_service instance before you can call run() for a second time.
I tried this: while (true) // PROBLEM: reentering this loop fails - why? { io_service.reset(); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); [...] Now the reconnection does not succeed either and after some tries I obtain an invalid file handle error. I guess I am missing some fundamental thing with asio ... Markus

Markus Werle wrote:
Bill Somerville <bill <at> classdesign.com> writes:
You need to call reset() on the io_service instance before you can call run() for a second time.
I tried this:
while (true) // PROBLEM: reentering this loop fails - why? { io_service.reset(); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); [...]
Now the reconnection does not succeed either and after some tries I obtain an invalid file handle error.
You didn't say how the first version or this version fails, an error message or some unexpected behaviour would help.
I guess I am missing some fundamental thing with asio ...
Markus
-- Bill Somerville

Bill Somerville <bill <at> classdesign.com> writes:
You didn't say how the first version or this version fails, an error message or some unexpected behaviour would help.
[... snipped nearly all read chars in 6s ...] 55565758595a5b5c5d5e5f60616Error: Connection did not succeed. Press Enter to exit ------------------- ------------------- Error: Connection did not succeed. Press Enter to exit Press Enter to exit Error: Das angegebene Dateihandle ist ung³ltig (last sentence: invalid file handle) Actual Code: // TestBoostAsio.cpp : Defines the entry point for the console application. // #include "stdafx.h" //int _tmain(int argc, _TCHAR* argv[]) //{ // return 0; //} /* minicom.cpp A simple demonstration minicom client with Boost asio Parameters: baud rate serial port (eg /dev/ttyS0 or COM1) To end the application, send Ctrl-C on standard input */ #include <deque> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/asio/serial_port.hpp> #include <boost/thread.hpp> #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <iostream> #ifdef POSIX #include <termios.h> #endif using namespace std; class minicom_client { public: minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device) : active_(true), baudRate_(baud), device_(device), io_service_(io_service), pSerialPort_(new boost::asio::serial_port(io_service, device)) { if (!pSerialPort_->is_open()) { cerr << "Failed to open serial port\n"; return; } boost::asio::serial_port_base::baud_rate baud_option(baud); pSerialPort_->set_option(baud_option); // set the baud rate after the port has been opened read_start(); } void write(const char msg) // pass the write data to the do_write function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_write, this, msg)); } void close() // call the do_close function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code())); } bool active() // return true if the socket is still active { return active_; } private: static const int max_read_length = 512; // maximum amount of data to read in one operation void read_start(void) { // Start an asynchronous read and call read_complete when it completes or fails pSerialPort_->async_read_some(boost::asio::buffer(read_msg_, max_read_length), boost::bind (&minicom_client::read_complete, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void read_complete(const boost::system::error_code& error, size_t bytes_transferred) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // read completed, so process the data cout.write(read_msg_, bytes_transferred); // echo to standard output read_start(); // start waiting for another asynchronous read again } else do_close(error); } void do_write(const char msg) { // callback to handle write call from outside this class bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written? write_msgs_.push_back(msg); // store in write buffer if (!write_in_progress) // if nothing is currently being written, then start write_start(); } void write_start(void) { // Start an asynchronous write and call write_complete when it completes or fails boost::asio::async_write(*pSerialPort_, boost::asio::buffer(&write_msgs_.front(), 1), boost::bind (&minicom_client::write_complete, this, boost::asio::placeholders::error)); } void write_complete(const boost::system::error_code& error) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // write completed, so send next write data write_msgs_.pop_front(); // remove the completed data if (!write_msgs_.empty()) // if there is anthing left to be written write_start(); // then start sending the next item in the buffer } else do_close(error); } void do_close(const boost::system::error_code& error) { // something has gone wrong, so close the socket & make this object inactive if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel() return; // ignore it because the connection cancelled the timer if (error) cerr << "Error: " << error.message() << endl; // show the error message else cout << "Error: Connection did not succeed.\n"; cout << "Press Enter to exit\n"; pSerialPort_->close(); if (pSerialPort_->is_open()) { delete pSerialPort_; pSerialPort_ = new boost::asio::serial_port (io_service_, device_); boost::asio::serial_port_base::baud_rate baud_option (baudRate_); pSerialPort_->set_option(baud_option); } active_ = false; } private: bool active_; // remains true while this object is still operating unsigned int const baudRate_; const string& device_; boost::asio::io_service& io_service_; // the main IO service that runs this connection boost::asio::serial_port * pSerialPort_; // the serial port this instance is connected to char read_msg_[max_read_length]; // data read from the socket deque<char> write_msgs_; // buffered write data }; int main(int argc, char* argv[]) { // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress // On other systems, you'll need to look for an equivalent #ifdef POSIX termios stored_settings; tcgetattr(0, &stored_settings); termios new_settings = stored_settings; new_settings.c_lflag &= (~ICANON); new_settings.c_lflag &= (~ISIG); // don't automatically handle control- C tcsetattr(0, TCSANOW, &new_settings); #endif try { if (argc != 3) { cerr << "Usage: minicom <baud> <device>\n"; return 1; } boost::asio::io_service io_service; // define an instance of the main class of this program minicom_client c(io_service, boost::lexical_cast<unsigned int> (argv[1]), argv[2]); // run the IO service as a separate thread, so the main thread can block on standard input while (true) // PROBLEM: reentering this loop fails - why? { boost::thread t(boost::bind (&boost::asio::io_service::run, &io_service)); boost::asio::io_service io; boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3)); timer.wait(); c.write('a'); boost::asio::deadline_timer timer2(io, boost::posix_time::seconds(3)); timer2.wait(); //while (c.active()) // check the internal state of the connection to make sure it's still running //{ // char ch; // cin.get(ch); // blocking wait for standard input // if (ch == 32) // ctrl-C to end program // break; // c.write(ch); //} c.close(); // close the minicom client connection t.join(); // wait for the IO service thread to close std::cout << "-------------------" << std::endl; io_service.reset(); } } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } #ifdef POSIX // restore default buffering of standard input tcsetattr(0, TCSANOW, &stored_settings); #endif return 0; }

Markus Werle <numerical.simulation <at> web.de> writes:
while (true) // PROBLEM: reentering this loop fails - why? { io_service.reset(); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); [...]
Now the reconnection does not succeed either and after some tries I obtain an invalid file handle error.
I guess I am missing some fundamental thing with asio ...
... or maybe it is some bug. http://stackoverflow.com/questions/541062/boostasioserialport-reading-after- reconnecting-device Any further comment? This code here still does not work: // TestBoostAsio.cpp : Defines the entry point for the console application. // #include "stdafx.h" //int _tmain(int argc, _TCHAR* argv[]) //{ // return 0; //} /* minicom.cpp A simple demonstration minicom client with Boost asio Parameters: baud rate serial port (eg /dev/ttyS0 or COM1) To end the application, send Ctrl-C on standard input */ #include <deque> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/asio/serial_port.hpp> #include <boost/thread.hpp> #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <iostream> #ifdef POSIX #include <termios.h> #endif using namespace std; class minicom_client { public: minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device) : active_(true), baudRate_(baud), device_(device), io_service_(io_service), pSerialPort_(new boost::asio::serial_port(io_service, device)) { if (!pSerialPort_->is_open()) { cerr << "Failed to open serial port\n"; return; } boost::asio::serial_port_base::baud_rate baud_option(baud); pSerialPort_->set_option(baud_option); // set the baud rate after the port has been opened read_start(); } void write(const char msg) // pass the write data to the do_write function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_write, this, msg)); } void close() // call the do_close function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code())); } bool active() // return true if the socket is still active { return active_; } private: static const int max_read_length = 512; // maximum amount of data to read in one operation void read_start(void) { // Start an asynchronous read and call read_complete when it completes or fails pSerialPort_->async_read_some(boost::asio::buffer(read_msg_, max_read_length), boost::bind (&minicom_client::read_complete, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void read_complete(const boost::system::error_code& error, size_t bytes_transferred) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // read completed, so process the data cout.write(read_msg_, bytes_transferred); // echo to standard output read_start(); // start waiting for another asynchronous read again } else do_close(error); } void do_write(const char msg) { // callback to handle write call from outside this class bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written? write_msgs_.push_back(msg); // store in write buffer if (!write_in_progress) // if nothing is currently being written, then start write_start(); } void write_start(void) { // Start an asynchronous write and call write_complete when it completes or fails boost::asio::async_write(*pSerialPort_, boost::asio::buffer(&write_msgs_.front(), 1), boost::bind (&minicom_client::write_complete, this, boost::asio::placeholders::error)); } void write_complete(const boost::system::error_code& error) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // write completed, so send next write data write_msgs_.pop_front(); // remove the completed data if (!write_msgs_.empty()) // if there is anthing left to be written write_start(); // then start sending the next item in the buffer } else do_close(error); } void do_close(const boost::system::error_code& error) { // something has gone wrong, so close the socket & make this object inactive if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel() return; // ignore it because the connection cancelled the timer if (error) cerr << "Error: " << error.message() << endl; // show the error message else cout << "Error: Connection did not succeed.\n"; cout << "Press Enter to exit\n"; pSerialPort_->close(); if (pSerialPort_->is_open()) { delete pSerialPort_; pSerialPort_ = new boost::asio::serial_port (io_service_, device_); boost::asio::serial_port_base::baud_rate baud_option (baudRate_); pSerialPort_->set_option(baud_option); } active_ = false; } private: bool active_; // remains true while this object is still operating unsigned int const baudRate_; const string& device_; boost::asio::io_service& io_service_; // the main IO service that runs this connection boost::asio::serial_port * pSerialPort_; // the serial port this instance is connected to char read_msg_[max_read_length]; // data read from the socket deque<char> write_msgs_; // buffered write data }; int main(int argc, char* argv[]) { // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress // On other systems, you'll need to look for an equivalent #ifdef POSIX termios stored_settings; tcgetattr(0, &stored_settings); termios new_settings = stored_settings; new_settings.c_lflag &= (~ICANON); new_settings.c_lflag &= (~ISIG); // don't automatically handle control- C tcsetattr(0, TCSANOW, &new_settings); #endif try { if (argc != 3) { cerr << "Usage: minicom <baud> <device>\n"; return 1; } boost::asio::io_service io_service; // define an instance of the main class of this program minicom_client c(io_service, boost::lexical_cast<unsigned int> (argv[1]), argv[2]); // run the IO service as a separate thread, so the main thread can block on standard input while (true) // PROBLEM: reentering this loop fails - why? { boost::thread t(boost::bind (&boost::asio::io_service::run, &io_service)); boost::asio::io_service io; boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3)); timer.wait(); c.write('a'); boost::asio::deadline_timer timer2(io, boost::posix_time::seconds(3)); timer2.wait(); //while (c.active()) // check the internal state of the connection to make sure it's still running //{ // char ch; // cin.get(ch); // blocking wait for standard input // if (ch == 32) // ctrl-C to end program // break; // c.write(ch); //} c.close(); // close the minicom client connection t.join(); // wait for the IO service thread to close std::cout << "-------------------" << std::endl; io_service.reset(); } } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } #ifdef POSIX // restore default buffering of standard input tcsetattr(0, TCSANOW, &stored_settings); #endif return 0; }

Markus Werle wrote:
Markus Werle <numerical.simulation <at> web.de> writes:
while (true) // PROBLEM: reentering this loop fails - why? { io_service.reset(); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); [...]
Now the reconnection does not succeed either and after some tries I obtain an invalid file handle error.
I guess I am missing some fundamental thing with asio ...
... or maybe it is some bug. http://stackoverflow.com/questions/541062/boostasioserialport-reading-after- reconnecting-device
Any further comment?
I think you should simplify your close semantics, passing an error code to your do_close() method is making it do more work than necessary. I don't understand the recreation of the serial_port object although I have not used the serial_port functionality myself. Also the close() method posts a do_close() call with a default constructed error object which is not handled correctly. I'm not sure why you need to use a loop in the main routine, if you want a server then use pending async calls to keep the io_service alive, if you want a client then perhaps errors should simply cause graceful termination. ... snip ... HTH -- Bill Somerville

Bill Somerville <bill <at> classdesign.com> writes:
I think you should simplify your close semantics, passing an error code to your do_close() method is making it do more work than necessary.
I am an absolute beginner with asio. I read through the docs and the examples, but obviously missed some points. The example I presented is stolen from the web from http://www.nabble.com/Simple-serial-port-demonstration-with-boost-asio- asynchronous-I-O-td19849520.html
I don't understand the recreation of the serial_port object although I have not used the serial_port functionality myself. Also the close() method posts a do_close() call with a default constructed error object which is not handled correctly.
In my GUI application I have 2 buttons: connect/disconnect. I simply want them to work and I probably want to switch ports during runtime. So here is my nitty-gritty task: Write a class with the following interface class SerialLineCommunicator { public: Connect (std::string const & PortName); Disconnect(); WriteToSerialPort(std::string const & Text); }; This class should write all received characters to std::cout. The problem I have is: The Disconnect() method seems to have some problems which I was trying to track down, but I got lost. Neither a close() nor a delete of thread or communicator resolves the issue. I found that the demo code I had stolen from the internet obviously has the same problem if adopted such that it terminates the connection and then retakes it. I have not found any other working demo code and the docs are not helpful to me either.
I'm not sure why you need to use a loop in the main routine, if you want a server then use pending async calls to keep the io_service alive, if you want a client then perhaps errors should simply cause graceful termination.
Can you probably provide some code that *should* work? I have no idea anymore. Thanks, Markus

I tried this:
while (true) // PROBLEM: reentering this loop fails - why? { io_service.reset(); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); [...]
Now the reconnection does not succeed either and after some tries I obtain an invalid file handle error.
It seems that you open your serial_port object in the constructor "minicom_client" wrapper. But you create it only once, before the loop starts, don't you? So after you explicitly close serial_port, no one opens it.

Igor R <boost.lists <at> gmail.com> writes:
It seems that you open your serial_port object in the constructor "minicom_client" wrapper. But you create it only once, before the loop starts, don't you? So after you explicitly close serial_port, no one opens it.
Ooops! So many tries and errors until such a stupid error. Now I got it. Rules are: - first start do_read(), - then start the thread - after termination of the thread an io_service_.reset() is mandatory. Otherwise subsequent thread starts lead to spurious behaviour. This was not clear to me and I dislike this behaviour ... Working code below. Markus ---snip--- // TestBoostAsio.cpp : Defines the entry point for the console application. // #include "stdafx.h" //int _tmain(int argc, _TCHAR* argv[]) //{ // return 0; //} /* minicom.cpp A simple demonstration minicom client with Boost asio Parameters: baud rate serial port (eg /dev/ttyS0 or COM1) To end the application, send Ctrl-C on standard input */ #include <deque> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/asio/serial_port.hpp> #include <boost/thread.hpp> #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <iostream> #ifdef POSIX #include <termios.h> #endif using namespace std; class minicom_client { public: minicom_client(boost::asio::io_service& io_service, unsigned int baud /*, const string& device */) : active_(true), baudRate_(baud), //device_(device), io_service_(io_service), pSerialPort_(new boost::asio::serial_port(io_service/* , device*/)) { } void write(const char msg) // pass the write data to the do_write function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_write, this, msg)); } void open(std::string const & portName) { pSerialPort_->open(portName); if (!pSerialPort_->is_open()) { cerr << "Failed to open serial port\n"; return; } boost::asio::serial_port_base::baud_rate baud_option (baudRate_); pSerialPort_->set_option(baud_option); // set the baud rate after the port has been opened read_start(); } void close() // call the do_close function via the io service in the other thread { io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code())); } bool active() // return true if the socket is still active { return active_; } private: static const int max_read_length = 512; // maximum amount of data to read in one operation void read_start(void) { // Start an asynchronous read and call read_complete when it completes or fails pSerialPort_->async_read_some(boost::asio::buffer(read_msg_, max_read_length), boost::bind (&minicom_client::read_complete, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void read_complete(const boost::system::error_code& error, size_t bytes_transferred) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // read completed, so process the data cout.write(read_msg_, bytes_transferred); // echo to standard output read_start(); // start waiting for another asynchronous read again } else do_close(error); } void do_write(const char msg) { // callback to handle write call from outside this class bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written? write_msgs_.push_back(msg); // store in write buffer if (!write_in_progress) // if nothing is currently being written, then start write_start(); } void write_start(void) { // Start an asynchronous write and call write_complete when it completes or fails boost::asio::async_write(*pSerialPort_, boost::asio::buffer(&write_msgs_.front(), 1), boost::bind (&minicom_client::write_complete, this, boost::asio::placeholders::error)); } void write_complete(const boost::system::error_code& error) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { // write completed, so send next write data write_msgs_.pop_front(); // remove the completed data if (!write_msgs_.empty()) // if there is anthing left to be written write_start(); // then start sending the next item in the buffer } else do_close(error); } void do_close(const boost::system::error_code& error) { // something has gone wrong, so close the socket & make this object inactive if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel() return; // ignore it because the connection cancelled the timer if (error) cerr << "Error: " << error.message() << endl; // show the error message //else //cout << "Error: Connection did not succeed.\n"; cout << "Closing serial port\n"; pSerialPort_->close(); /*if (pSerialPort_->is_open()) { delete pSerialPort_; pSerialPort_ = new boost::asio::serial_port (io_service_, device_); boost::asio::serial_port_base::baud_rate baud_option (baudRate_); pSerialPort_->set_option(baud_option); }*/ active_ = false; } private: bool active_; // remains true while this object is still operating unsigned int const baudRate_; //const string& device_; boost::asio::io_service& io_service_; // the main IO service that runs this connection boost::asio::serial_port * pSerialPort_; // the serial port this instance is connected to char read_msg_[max_read_length]; // data read from the socket deque<char> write_msgs_; // buffered write data }; int main(int argc, char* argv[]) { // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress // On other systems, you'll need to look for an equivalent #ifdef POSIX termios stored_settings; tcgetattr(0, &stored_settings); termios new_settings = stored_settings; new_settings.c_lflag &= (~ICANON); new_settings.c_lflag &= (~ISIG); // don't automatically handle control- C tcsetattr(0, TCSANOW, &new_settings); #endif try { if (argc != 3) { cerr << "Usage: minicom <baud> <device>\n"; return 1; } boost::asio::io_service io_service; // define an instance of the main class of this program minicom_client c(io_service, boost::lexical_cast<unsigned int> (argv[1])/*, argv[2]*/); // run the IO service as a separate thread, so the main thread can block on standard input while (true) // PROBLEM: reentering this loop fails - why? { std::string port = argv[2]; c.open(port); boost::thread t(boost::bind (&boost::asio::io_service::run, &io_service)); boost::asio::io_service io; boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3)); timer.wait(); c.write('a'); boost::asio::deadline_timer timer2(io, boost::posix_time::seconds(3)); timer2.wait(); //while (c.active()) // check the internal state of the connection to make sure it's still running //{ // char ch; // cin.get(ch); // blocking wait for standard input // if (ch == 32) // ctrl-C to end program // break; // c.write(ch); //} c.close(); // close the minicom client connection t.join(); // wait for the IO service thread to close std::cout << "-------------------" << std::endl; io_service.reset(); } } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } #ifdef POSIX // restore default buffering of standard input tcsetattr(0, TCSANOW, &stored_settings); #endif return 0; }

Rules are:
- first start do_read(), - then start the thread - after termination of the thread an io_service_.reset() is mandatory. Otherwise subsequent thread starts lead to spurious behaviour. This was not clear to me and I dislike this behaviour ...
ASIO reference explicitly states that you must call io_service::reset() before subsequent call to io_service::run(). However, you didn't have to exit the thread that runs io_service::run() - this's just matter of design. In the asio examples you can find other design choices.
participants (3)
-
Bill Somerville
-
Igor R
-
Markus Werle