> I'm trying to get some synchronisation working between several child > processes, using a counting semaphore. > Probably I'm doing something wrong, because I get errors when releasing the > semaphore, stating that the handle is invalid. > Because the release works the first time, I think I must do something with > the inherited filehandles. There are several problems with Win32::Semaphore and fork()ed threads. And a few with your code. 1) The docs say that open() is a CLASS method, not an instance one. And that is gives you an object as its return value. So you should use it like this : $semchild = Win32::Semaphore->open($name) or die "Cannot open semaphore $!"; So you should create the semaphore in the parent thread and then create new objects with Win32::Semaphore->open($name) in the children. AND DO NOT USE THE OBJECT CREATED IN THE PARENT FROM THE CHILDREN. Also for whatever reason the ->release() did not like $prev preset to 999. Leave it undefined. 2) Since the parent wait()s for the semaphore it should also release() it. Otherwise you run out of semaphores pretty quickly. 3) You have to make sure your children will not call DESTROY() on the semaphore object you created in the parent. It would then become unusable even in the main thread !!! The DESTROY() calles some Win32 API functions and of course since all threads have the same Win32 API handle in the $sem object, it doesn't matter which one calls the DESTROY(). Of course you can't undef $sem; in the children since that would call the DESTROY(). You have to do it like this : bless $sem, "EmptyObjectNotDefinedAnywhere"; undef $sem; The first line unties the object from the Win32::Semaphore class so the second may safely delete the perl part of it. I'm sure you will need this hack in many other places with many other object as well with fork()ed threads. Actualy you should do this each time you create an object BEFORE fork(), but cannot allow more threads to be able to actualy destroy the object. The reason is that the perl part of the object was cloned (every child has its own copy) but the external part (something the system or the DLL keeps and you only point to it with a handle or pointer) was NOT. So even if it might be safe to use the object it is NOT SAFE TO DESTROY it. The code should look something like this : use Win32::Semaphore; $sem = Win32::Semaphore->new($initial,$maximum,$name); for (my $i = 1; $i < 25; $i++) { $sem->wait(); if (fork()) { #child bless $sem, "EmptyObjectNotDefinedAnywhere"; undef $sem; $semchild = Win32::Semaphore->open($name) or die "Cannot open semaphore $!"; $semchild->wait(500) or die "Child $$ : Wait failed\n"; ... do something $semchild->release(); exit; } else { #parent $sem->release(); } } Jenda P.S.: Fixed version of your code is attached. Actualy I'd like to thank you. I did not notice this module could be used so easily to restrict the number of running children alive at a time. P.P.S.: (for Sarathy) I believe some note about the DESTROY() problem and possible solution should be part of perlfork(). I don't know how to write it so that it's understandable. == [EMAIL PROTECTED] == http://Jenda.Krynicky.cz == : What do people think? What, do people think? :-) -- Larry Wall in <[EMAIL PROTECTED]>
use strict; use Win32::Semaphore; use vars qw($sem $semchild); $SIG{CHLD} = sub {wait()}; sub test_sem(); exit test_sem(); #package EmptyClass; #package main; sub test_sem() { my ($pid, $initial, $maximum, $name, $wksta) = (0,5,5,'SEINPAAL',''); my ($sec,$min,$hour); # Create counting semaphore for a maximum of 5 outstanding childs # each child decrements count with 1 at exit, enabling parent to spawn another $sem = Win32::Semaphore->new($initial,$maximum,$name); for (my $i = 1; $i < 25; $i++) { $sem->wait(); #wait for semaphore to become available print "Spawning child for $i\n"; $pid = fork; if ($pid == 0 ) { # Child bless $sem, "EmptyClass"; die "cannot fork: $!" unless defined $pid; $semchild = Win32::Semaphore->open($name) or die "Cannot open semaphore $!"; $semchild->wait(500) or die "Child $$ : Wait failed\n"; ($sec,$min,$hour,) = localtime(time); print sprintf("Child %d starts on %02i:%02i:%02i\n",$$, $hour, $min, $sec); for (my $j = 0; $j < 100000; $j++) { # Do some work my $k = $j / 2 } ($sec,$min,$hour,) = localtime(time); print sprintf("Child %d ends on %02i:%02i:%02i\n",$$, $hour, $min, $sec); my ($inc, $prev) = (1); # $semchild->release() or die "Could not release semaphore : $^E"; $semchild->release($inc,$prev) or die "Child $$ could not release semaphore : $^E"; print "Child $$ released on $prev\n"; exit; } else { my ($inc, $prev) = (1); $sem->release($inc,$prev) or die "Father could not release semaphore : $^E"; } } }