Boost logo

Boost :

From: Pedro Lamarão (pedro.lamarao_at_[hidden])
Date: 2005-06-09 17:00:31


Simon Richter wrote:

> Yes, that is the plan (name resolution is not implemented yet). A socket
> stream can be constructed with an optional reference to a "manager"
> class (which is the select() wrapper, basically), which holds a set of
> resolvers (similar to locale facets). If a manager object is given, it
> is asked for a resolver, which is given the query and a method to call
> on completion, then control is transferred back to the caller. If no
> manager object is given, a resolver is instantiated, the name resolved
> and the resolver destroyed (=> you need a manager for nonblocking I/O).
>
> The resolver is usually called by the manager when the application
> enters its idle loop, so applications may not wait for connections to
> get established when they use a manager object.

But how do you actually implement asynchronous name resolution? Are you
using asynchronous primitives from the operating system, or are you
holding a worker thread?

>>But the need for the existence of a "socket" class is questionable; the
>>IO primitive in the C++ standard library is the streambuf; ugly or not,
>>it provides a complete "client" for a networking library.
>
>
> My streambufs hold a pointer to an abstract "impl" class which
> represents the actual socket. Concrete subclasses of that exist for
> different socket types, so new address families can be added at runtime
> (currently, only through my plugin interface, which I wrote about to the
> list a few days ago).

So each "implementation type" is strongly connected to a value for the
family parameter?

Something like this?

template <typename FamilyT>
class socket;

Such a definition would make a "socketbuf" subclass of streambuf have
one extra template parameter; and we would still have to specify at
least the value for "style" to select a transport protocol.

>>That auto_ptr is there because stream objects are not copyable, and the
>>purpose of that "listener" is to internalize the "accept" primitive and
>>the stream constructor.
>
>
> They are not copy-constructible, that is different from not copiable
> :-). A new streambuf can be attached to an existing stream, as long as
> care is taken to properly destroy the streambuf on stream close).

streambuf's are neither copy constructible nor assignable, so, they are
effectively non-copyable.

You don't copy a streambuf while "personalizing" an iostream class, you
pass a pointer to a streambuf. That's not copying. :)

>>I'd be happier to return an "rvalue reference" there, if we already had
>>such a thing.
>
>
> Hrm, they could be made copy-constructible, I think, the benefit of that
> would however be questionable.

If you're referring to the stream stuff, you'd have to change the
standard to make anything copyable.

If you're referring to the "implementation type" for the socket, that
would be unwise, as it would break RAII.

>>In the example/ directory of the socketstream project, check out the
>>asynch_resolver.h file; there's an asynchronous version of the resolver
>>class there implemented using boost::thread.
>
>
> I avoid spawning new threads from library functions like the plague, as
> it is difficult to properly collect those threads in case of an
> exception etc.

Well, then you would require asynchronous primitives from the operating
system... There *are* such primitives, of course.

>>I suspect there's little hope of doing anything different than keeping a
>>std::vector or std::list of whatever networking object we're holding,
>>and creating the proper structure for select() or poll() when calling
>>the blocker method. That might make the blocker method O(n) on the
>>number of networking objects...
>
>
> I think the manager object should have a list of pointers to the streams
> it monitors and have a callback registered with them so the stream is
> scheduled for removal on stream destruction (we cannot unregister the
> stream right away as there might be iterators in the list of pointers
> that we'd invalidate).

Hum... I was referring to the access to the poller interface.

Take select, the simplest, for instance.
The argument for select is a fd_set of descriptors, managed by macros
take descriptors as arguments.

But we won't be holding a fd_set of descriptors, but a standard
container of references to stream objects.

So this might be a selector class:

class selector : public std::vector<iostream*>
{
public:

    void
    wait (timeval* timeout)
    {
        fd_set fds;
        for (iterator i = this->begin(); i != this->end(); ++i)
            FD_SET((*i)->handle(), &fds);
        ::select(fds, timeout);
    }

};

Of course, the prototype for select is much more complicated; it would
be wiser to use poll, but then I'm unsure if poll is available everywhere.

-- 
Pedro Lamarão
Desenvolvimento
Intersix Technologies S.A.
SP: (55 11 3803-9300)
RJ: (55 21 3852-3240)
www.intersix.com.br
Your Security is our Business

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk