5

I am to implement a (hopefully) robust asynchronious serial rs232 data transmission (via USB) - for both windows and linux, including esp. that nice embedded system called beagle bone black.

In the end I just want to be able to (compatibly) talk rs232 with robust deadline-timeouts, cancel() reset() etc. to not crash/hang when e.g. the tx or rx line disconnects accidently. So sure, I could simply copy/paste/adopt existing examples. But I also would like to become more enlightened ;-)

I decided to use boost:asio::serial_port. Now while reading the docs, I am confused about this two classes (well three with the typedef serial_port):

serial_port_service - Default service implementation for a serial port.

class serial_port_service : public io_service::service

basic_serial_port - Provides serial port functionality.

template< typename SerialPortService = serial_port_service>
class basic_serial_port :
  public basic_io_object< SerialPortService >,
  public serial_port_base

So faar I see, that I need a boost::asio::io_service to construct either boost::asio::serial_port or serial_port_service. I think I have understand the basic approach how asio does the job, like bespoken in e.g. this examples .

OK serial_port_service derives from io_service, its ctor takes an io_service, and its interface also offers those memberfuncs of basic_serial_port.

For me it looks like it's a io_service that also implements a basic_serial_port - what is the reason for having both classes? When to use the one when the other? Not sure about possible usecases, and what about this serial_port typedef. Maybe (well obviously) I am missing something - someone can give me more light?

Community
  • 1
  • 1
tverrbjelke
  • 1,234
  • 10
  • 15

1 Answers1

7

Often, the application should use the I/O object. In this case, that would be boost::asio::serial_port.


The various classes are used to separate responsibilities and abstractions. The similarity in names can be confusing, so the documentation is very careful in its naming. The documentation states:

Class io_service implements an extensible, type-safe, polymorphic set of I/O services, indexed by service type. An object of class io_service must be initialised before I/O objects such as sockets, resolvers and timers can be used. These I/O objects are distinguished by having constructors that accept an io_service& parameter.

I/O services exist to manage the logical interface to the operating system on behalf of the I/O objects. In particular, there are resources that are shared across a class of I/O objects. For example, timers may be implemented in terms of a single timer queue. The I/O services manage these shared resources.

To explain this in context of serial ports:

  • The io_service provides an event processing loop and manages I/O services, such as serial_port_service.
  • The serial_port is an I/O object that provides an interface to perform serial port related operations. The implementation is very basic:
    • Determines how information is to be returned to the caller: throw if an error occurs, populate a std::future, suspend a coroutine, etc.
    • Defers the actual work to the serial_port_service, its I/O service.
    • Provides RAII semantics. When the serial_port is destroyed, it will cancel outstanding asynchronous operations and close the serial_port.
  • The serial_port_service is an I/O service:
    • It provides an abstraction and implements the platform specific API.
    • It is shared amongst serial_ports that use the same io_service. When the serial_port is created with an io_service, the serial_port will use an existing serial_port_service registered to the io_service or create and register a new serial_port_service with the io_service.
    • It functions as an factory for an I/O object's implementation. For a serial_port, this is likely a basic file descriptor or handle.
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Hm.. so serial_ports sharing the same io_service also share the same serial_port_service. One cancel() will also cancel all other serial_ports of that io_service then, right? So when I want to read/write to different ports (e.g. com3 com4) I use different io_services in serial_port ctors - otherwise they would interfere from each other? I would do something like: serial_port serial1(io_stream1); serial1.open("com3", ec); serial_port serial2(io_stream2); serial2.open("com4", ec); Then serial2.cancel() would not cancel serial1? – tverrbjelke Oct 27 '14 at 14:11
  • When doing async_read_some - is there any chance to use a deadline-timer which cancelS *only* the overdue read operation it is related - I mean without canceling also the ongoing write operations? Until now I only found serial_port.cancel() - and no way to cancel individual async_read_some operations... – tverrbjelke Oct 27 '14 at 14:18
  • @tverrbjelke Calling `cancel()` on an I/O object will cancel all outstanding operations on that distinct I/O object and should have no observable affect on any other I/O object, even those that use the same `io_service` and I/O service. – Tanner Sansbury Oct 28 '14 at 02:02
  • So do I correctly understand you? When I have opened two serial ports s1(Com3) and s2(com4) with different io_services. Then a async_read_some timeout handler does s1.cancel() it would cancel all ongoing read *and* write operations on com3, but it would not bother com4. Is there any way to let an related timeout handler watching async_read_some() kill only it's (s1) *read* operations? Without also killing any parallel async_write_some() operations on s1? How to asynchroniously check for "data ready to be read" without deadline_timer canceling everything? – tverrbjelke Oct 28 '14 at 12:23
  • 1
    @tverrbjelke The statements are correct, but I want to stress that canceling a distinct I/O object (`s1`) will have no affect on another distinct I/O object (`s2`), regardless of which `io_service` they use. There is no way to cancel a specific type of operations on an I/O object. To be asynchronously informed of when data is ready without actually reading it, try using [reactor-style](http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/overview/core/reactor.html) operations. Also, the `async_write_some()` handler could initiate another `async_write_some()` operation if canceled. – Tanner Sansbury Oct 28 '14 at 17:57