2009/10/9 Andreas Altergott <alterg...@mira-consulting.net> > Hi, >
Hi Andreas > this will be especially interesting for dolman :-) > dolmen ! > as already described in previous emails there is a big problem when > using POE::Wheel::Run::Win32 with Win32::Daemon as a windows service. > Your service will get a termination request from windows, as soon as any > child process terminates. > Here is the bug I submitted in the POE::Component::Daemon::Win32 queue, for reference: https://rt.cpan.org/Ticket/Display.html?id=50020 > This happens because POE::Wheel::Run::Win32 is using the fork() method. I do not know how exactly ActiveStates Perl handles the fork() method, > but it does it different than the system() method. The system() method > also creates a child process, but it waits for the child until it > terminates. This does not initiate a termination request to the > service. Although both do create sub processes. > > Speaking in windows terminology one has to say threads instead of > processes. So system() is using fork() according to the perldocs. And > fork() tries to do a real fork() if it is available. On windows it is > not. So it has to create a thread. I don't know how it does terminate > a thread though. It must be different when you use system(), else it > wouldn't terminate your service. > > This description is incomplete. POE::Wheel::Run{,::Win32} on Win32 creates an external process using Win32::Job and uses the thead created with fork() to monitor the job (->watch) and report its status. Win32::Job->watch() does polling (which is awkward when you live in the POE world) to monitor the subprocess. > Luckily, using threads is not a blocking process. It means that your > POE program can continue working, while the thread is also doing it's > work. The only drawback is, that your programs will be windows > specific. You can not do a 'use threads;' inside of eval, require or > any other trick to make it system independent. > > o http://perldoc.perl.org/threads.html > > The fast solution is to use threads instead of POE::Wheel::Run::Win32. > I think that POE::Wheel::Run{,::Win32} already does this. > > use threads; > > my $thr = 0; > > sub thread { > return(system("C:/Progs/any.exe")); > } > > ... > > # inside a POE subroutine that's getting called over and over again > unless ($thr) { > print("creating thread\n"); > $thr = threads->create('thread'); > } > > if ($thr->is_joinable()) { > print("thread returned " . $thr->join() . "\n"); > $thr = 0; > } > > ... > > I've been reading in some previous mails, that POE::Wheel::Run and > POE::Wheel::Run::Win32 have been merged. This is nice to hear :-) > But in this case it complicates things. Because now POE::Wheel::Run is > system independent by using fork(). If you look at the code you'll see that the code path on Win32 is quite different. So "merged" does not means "does the same". Also, the aim of the merge is to make your code system independent (of course this is not the case when using Win32::Daemon too). As described this is not possible > with Win32::Daemon. So either it should be noted somewhere, that > POE::Wheel::Run is not usable from a windows service, or it should be > split again :-p into POE::Wheel::Run::Win32 using threads instead of > fork(). ;-) > > Maybe this issue is easier to fix, by attacking fork() itself. > Well, you do not strictly need POE::Wheel::Run, if you do use threads. > They are not blocking anyway, by doing the test is_joinable(). > > I'm not sure if this concerns only Win32::Daemon or if it is a global > windows service problem with Perl using fork(). > I think that the problem is in Win32::Daemon, for which we don't have the source (his author, Dave Roth seems to have disappeared without publishing the source for its latest binary release). See the bug where I have posted a simple test case that does not involves neither POE, POE::Wheel::Run{,::Win32}, Win32::Job or POE::Component::Daemon::Win32. I managed to workarounk the bug by separating the program in two process: - a service wrapper that uses Win32::Daemon, open a server socket and launches the real service as a subprocess. When it receive a command from the service manager it forward it to the child as a message on the socket. - the real service : connects to the service wrapper socket when it starts and monitors it with a POE::Component::Client::TCP I wanted to use Win32 pipes instead of Internet sockets, but I've found nothing to monitor them with POE (there is no select() on file/pipe handles in the Win32 world). Anyway this works because there is exactly only one process launched from the wrapper, and when the child exits, we want the service to stop which matches the state change that Win32::Daemon sends. However this is a really ugly and we will still need one day a fixed Win32::Daemon. Olivier Mengué (dolmen).