Larry Lewis wrote:
> I can't speak for the POCO maintainers, but in my opinion, the scope of your suggestions is immense.
If you strike out the bulk of the land grab on upstream and pushing the
existing embedded contrib code into a contrib module, is that really
true? API compatibility can be retained for neary all of it, except for
some uses of AutoPtr which are *likely* to be error-prone (and a
compatibility #define would handle transition) and some broken
assignment 'opportunities' between the different int64 typedefs in
Timestamp. The config and command line stuff could potentially cause
breakage but it may still be possible to offer a compatibility switch,
or just provide an alternate system.
The problem here is that I started out to write a book on using POCO to
write a particular style of networked system and fairly rapidly realised
that more of it was dedicated to gotchas than I was comfortable with. I
still strongly prefer it to ACE and Boost but I've observed little API
evolution rather than extension and that's why I've been rather firm in
pushing back on suggestions to code in isolation into sandbox and then
hope for the best in terms of acceptance.
> This is why I'm trying to whittle this down to an initial concrete proposal involving async network IO, which seems to be at the core of both your and my requirements.
>
Indeed. Though a desire to leverage the libevent HTTP and aDNS might tip
the balance.
Also - I'm not actually convinced are requirements line up. Are you
*wanting* a scaled reactive system per se, or just scaled IO? For me,
the proactive model is a nicer abstraction and I can just implement on a
reactive model on unixen. What sort of problem do you want to solve?
For me, its intranet applications with custom session-oriented
protocols. It would be nice to build a competitive web server, but only
as a technology demonstrator from my point of view. The developers
looking at servlet and Ajax might feel differently but they seem to have
been accepting the status quo without making a fuss so presumably their
ambitions are aligned with a blocking threaded system. You can get quite
far with that after all - I don't recall Java being a complete disaster
pre NIO.
Let's keep talking. I suspect a fork is the most attractive way to deal
with different objectives in terms of at least trying changes (I'd say
'fixes' but I'm realistic that they may not work out). I have to decide
how I'll license and/or expose changes to the core (almost certainly the
same license) and works that are essentially done on my own (which might
not be). I'm going to have to think about this.
> No preference on libevent vs libev. It's unfortunate those communities couldn't agree on a technical approach and ended up developing parallel libraries (libev even has a libevent compatibility header). Let's not make that mistake here.
Is it a mistake? libevent seemed all but moribund until libev showed up and they keep each other honest. They don't seem to share code in the way that *BSDs do, which is a shame. I'd like to retain some of that, without the pain observed by failed would-be Linux kernel submitters who got there second or voted down, or whatever.
> If you disregard the existing IO work, what Foundation or Net elements really need to change to support this?
>
Socket, Pipe, Process. Ans assorted Impls. It might be that the existing
API forms can remain but retaining exactly the same semantics may not be
desirable.
It might be possible to use an IO Manager as a factory for 'socket'
abstractions of some form that are more suited to aAIO, but I'm
concerned that this will be confusing unless you can get a Net::Socket
from them like you can with java NIO, and I'd prefer that a socket you
get with 'new Socket()' be closely related to a socket that you get from
'aReactorManager.newSocket()' and one that you get from
'anAIOManager.newSocket()'. I'm uncomfortable with an assumption that
such unification can be achieved and still retain the same API and
semantics as the existing Net::Socket.
And I'm doubly uncomfortable with doing too much implementation until
there's some direction. I started out on a simplex stream based
implementation a while ago but it seemed to get rather ugly. And a
duplex looks-like-a-socket approach ends up with kludges for pipes and
processes. I'm fishing for others' experience really.
> How about throwing together some API to see how the pieces might fit together?
>
At the moment I'm looking at select integration. I don't think anything
will fly without an ability to integrate reactive loops, given that
there are still systems that resist threading.
It looks like this (edited for brevity):
class ReactorInterest
{
public:
ReactorInterest() ;
ReactorInterest(const ReactorInterest& that) ;
ReactorInterest(bool forRead, bool forWrite, bool forExcept) ;
ReactorInterest& operator=(const ReactorInterest& that) ;
bool isNull() const ;
ReactorInterest filter(const ReactorInterest& events) const ; //
aka union
bool isRead() const ;
bool isWrite() const ;
bool isExcept() const ;
void setRead(bool onOff) ;
void setWrite(bool onOff) ;
void setExcept(bool onOff) ;
} ;
class IWakeup
{
public:
typedef void* Closure ;
virtual void onWakeup(Closure closure, const ReactorInterest*
pEvents) = 0 ;
} ;
class IReactorIntegrator
: public IObject
{
public:
typedef Poco::Timestamp Timestamp ;
typedef poco_socket_t Descriptor ;
virtual void onRegisterDescriptor(Descriptor fd, const
ReactorInterest& interest, IWakeup* wakeup, IWakeup::Closure closure) = 0 ;
virtual void onForgetDescriptor(Descriptor fd) = 0 ;
virtual void onDescriptorInterestChange(Descriptor fd, const
ReactorInterest& interest) = 0 ;
virtual void onTimerRequest(const Timestamp& nextTimeout,
IWakeup* wakeup, IWakeup::Closure closure) = 0 ;
} ;
class BaseReactor
: public IReactorIntegrator
{
public:
virtual void onRegisterDescriptor(Descriptor fd, const
ReactorInterest& interest, IWakeup* wakeup, IWakeup::Closure closure) ;
virtual void onForgetDescriptor(Descriptor fd) ;
virtual void onDescriptorInterestChange(Descriptor fd, const
ReactorInterest& interest) ;
virtual void onTimerRequest(const Timestamp& nextTimeout,
IWakeup* wakeup, IWakeup::Closure closure) ;
/** run
*
* Perform one round of integration, calling any registered
callback functions.
*
* It returns true if there was anything to do - which means
that there was at
* least one file descriptor event callback, OR a timer
callback. Returns FALSE
* if there was a signal.
*/
virtual bool run() = 0 ;
} ;
Now:
- This 'interface' has a value type in it, which might cause concern.
- I still haven't defined any mechanism to specify interaction with
signals.
- I'm not ref counting callbacks etc
- I'm a little uncomfortable exposing Poco types here rather than raw
platform types
While I have a Poco::Net::Socket::select implementation, I have yet to
do anything about signals. The Poco select swallows signals and keeps
waiting until its timer expires, but this won't go down well on a
'principle of least surprise' basis for old timers and I feel I probably
have to do something about this.
I'm going to have to line this up with the what the Poco::SignalHandler
can provide, and I haven't attempted this yet.
That class has some issues in that it doesn't handle Win32 console
events, and the SignalHandler::jumpBufferVec() makes the 'usual'
assumption that there is just one thread on Poco that wasn't *started*
as a Poco::Thread. Happy that your thread private storage shared with
other threads? Is this addressed by the scripting proposals? :-(
|