Forsaken has asked for the wisdom of the Perl Monks concerning the following question:

dear Monks, once again I am in need of your infinite wisdom.

This is not so much a matter of not knowing how to do something, but more of how to do it properly from an OO point of view.

The situation is as follows: I'm writing a module that will allow the creation of 1 or more irc connections. I've decided to treat the connections themselves as the objects, and all the data and settings as properties of that specific object. So far so good. For each connection the calling script uses the ->new method, and as many connections as desired can be created, and the proper references are returned in classic OO fashion.

But it is at this point that my troubles begin. The very nature of such an irc connection requires a continuous loop. The easy solution is to simply keep an internal array of all the objects, and loop through them one at a time, directing data in and out as required, but somehow this approach *feels* wrong, not properly OO. What I'm doing right now is allow the calling script to call ->run directly on an object, meaning that irc connection gets placed in a loop, data flows happily in both ways, but any other connections just sit there and eventually perish, since from the moment the loop starts, the script is effectively frozen.

From what I can see, I have 3 options at this point:
- to make 1 global run method that kicks all connections into gear using an internal array of connections. it feels wrong somehow, but it does work. obviously this would have to be the last command in the calling script, since it's an endless loop.
- to make a run method that is not a loop, but instead handles all the buffered data at that time, and that's it. this would mean that the responsibility of looping through each of the connections again and again lies with the calling script. doesn't feel right somehow either.
- to somehow split off the connection once ->run has been called, perhaps even into a separate process(no idea how to do that, any tips would be greatly appreciated), thereby allowing the calling script to continue and eventually even finish.

Any pe(a)rls of wisdom thrown my way will be mightily appreciated.

Don't forget Rule One...

Replies are listed 'Best First'.
Re: OO theory question
by Abigail-II (Bishop) on Jun 09, 2004 at 15:00 UTC
    Well, you basically will need somewhere in your program a piece where all the irc connections work together (unless you want to give each irc connection its own separate thread or process) - and that's called the select loop. OO is nice for modelling data, but it doesn't do the program flow for you. Deep down, the heart of your program will be a simple loop:
    while true do is there anything to read or write on my IRC connections? if so, read or write, else maybe do something else done
    There are several modules out there that will help you implement it - but you'll need the loop.

    Abigail

      basically option 1 then. in that context, would it make more sense to have the ->new method do a simple bless, and implement another method, perhaps ->connect, that creates connections as properties of the object returned by ->new?
        IMO, a constructor should only construct, and not do anything else. Otherwise, your subclassing classes will be forced to call your constructor, and that's a major pain in the ass if the subclass wants to do MI.

        Abigail

Re: OO theory question
by bart (Canon) on Jun 09, 2004 at 14:51 UTC
    This sounds like a job for POE, to me.
      I used to use POE, in fact. Problem is that I like to do things by hand. It's not that I intend to write the next greatest thing or anything, P::C::I absolutely rocks, but I just like to write things myself so I know what's happening under the hood.
        Welp nothing stopping you from making your own P::C::I to handle the irc interface. Just use POE for the multitasking.


        -Waswas
Re: OO theory question
by stvn (Monsignor) on Jun 09, 2004 at 21:03 UTC

    Of your 3 options, I would throw out option 2 as it places to much responsibility on your calling script IMO. As for the other options, its a matter of taste.

    Option 1 is similar to the select-style of doing IO. The idea of select is that you have a tight loop in a single process, on each round of the loop you call select and get back information on which file handles are read for reading, which are ready for writing and which have errors and then do what is appropriate. It is basically the best way to do asyncronous non-blocking IO without spawning multiple processes. This technique tends to be more management intensive, but it's less resource intensive than threads or forking processes (aka - your 3rd option).

    In order to do this in the most OO way, my suggestion would be to make run a class method, which references a package level array of all your connection instances. Or if that seems un-OO to you, you can make a factory class for your connections, which will retain a reference to all connections it dispenses and then have your factory "run" everything. With OO, as with perl, TIMTOWTDI.

    Your 3rd option is the more common way to do about these things (at least in my experience). Apache works this way (yes I know I am greatly simplifying Apache), as do most web servers. The benefits are that you can code your connection objects in such a way that you need not worry about resource (net-connection) sharing and asyncronous behaviour. The forked-process/thread takes care of that for you. But, as I mentioned above, its much more resource intensive in terms of memory and CPU.

    The good thing about this technique is that your connection objects can become simplier since they have no need to worry about hogging resources. You will still need to write some code to fork a process or spawn a thread for each one, but once that is done, the processes/threads take care of a lot of the "management" stuff that you would do by hand with option 1.

    If you do go for option 3 (which I would recommend), you should look for some thread/fork managers on CPAN. Thread::Pool of Parallel::ForkManager come to mind.

    -stvn
      Edit2, wooohah! iet ies alive! now if only i can find a way to make it stop dying because the main script ends i'm all the way there :/

      Edit, problem solved by using threads->create(\&_run); instead

      well, i went for your suggestion concerning threads, and all seems to be fine, except for one thing, the thread doesn't return control to the calling subroutine :(

      I've spent quite a bit of time reading tutorials and the like, and they all tell me that when using the threads module the calling script should continue normally after spawning off a thread...here's the bit of code that i use:
      sub run { my $self = shift; $self->{thread} = threads->create($self->_run); print "does this ever show up?\n"; }
      The print statement never gets executed, as the thread takes control of the entire script. Any ideas?

        Sorry, threads aren't my thing, so I am not the best person to answer your question. But I think showing us your _run method might be helpful in helping solve this problem. I can give you a basic example of how one would do this with fork:

        sub run { my ($self) = @_; my $PID = fork(); # fork returns undef if it fails (defined $PID) || die "Could not fork successfully"; # fork returns the Process ID of the new process it # has created to the parent process, and 0 to the # child process, so if we have a "true" value in $PID # then we are in the parent if ($PID) { # so we let the parent store the PID # of the process which is "_run"-ning $self->{PID} = $PID } else { # otherwise we are in the child, so we # should call _run $self->_run(); # we sure to call exit here so our child # process cleans up nicely exit(); } } # you can call stop from the parent # process to kill the child, you can adapt # this to send any number of signals to # the child depending upon your needs sub stop { my ($self) = @_; kill "INT", $self->{PID}; }
        Here are a couple of links to relevant documentation:

        -stvn
Re: OO theory question
by pbeckingham (Parson) on Jun 09, 2004 at 15:10 UTC

    Is threading an option available to you? It would let your connection objects operate in a more natural way. If your Perl supports it (5.8 or later recommended), and if your familiarity with threading, or your tolerance for a bit of a learning curve is sufficient, then perhaps consider threads.

      i'm running 5.8.3, Activeperl to be exact. I'm not scared of learning something new, but my knowledge of threading is, well, negligible :-) I've been doing a little searching on how I might go about this, but so far I haven't really been able to find a tutorial or explanation that offers me great enlightenment.

        Threading is a good thing to learn. It's becoming more prevalent, and as the implementations mature, and multiple CPU machines become more popular, it will become a more valuable skill. Or put another way, you'll need to be good at threads one day.

        Try perlthrtut, and of course, PerlMonks - there are many tutorials, helpful hints, and patient folks willing to help.

        Now the caveats - if this project has a challenging deadline, or if you anticipate difficulties, or if this is a high-profile app with low tolerance for bugs, you may want to back off threads.