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

Greetings Fellow Monks,

 I believe I have an interesting problem with the implementation of the Singleton design pattern.

 Essentially I'm trying to keep a cache of meta-data read from MP3 files, within a fork()ing server.

 To do this I create a singleton "cache object" within the parent, then modify the contents of it within the child process.

 Unfortunately my changes to the singleton object in the child processes are not reflected within the parent - So unsure how to proceed, I was under the impression that the singleton reference would be valid for both.

 This is my creation code:

my $oneTrueSelf; sub new { if($oneTrueSelf) { return $oneTrueSelf; } else { my $type = shift; my $this = {} $oneTrueSelf = bless $this, $type; return $this->new(); } }

 Is there an obvious way to proceed? Or do I have to brave the scary shared memory magic?

Steve
---
steve.org.uk

Replies are listed 'Best First'.
Re: The Singleton design pattern and fork();
by tall_man (Parson) on Feb 22, 2003 at 05:38 UTC
    I'm afraid that's right. A fork makes a complete, independent copy of the memory state of the parent. Your singleton has become a doubleton and there is no connection between the copies. You'll have to share memory, use threads, or do interprocess communication (such as sockets).

       Thanks for the reply, and for confirming my suspicion.

       I was assuming that because the $oneTrueSelf' reference would be present in both the parent and the child changes would be reflected in both.

       (Hmm I guess this doesn't really qualify as a Perl question at all, just a misundertanding of how processes work)

      Steve
      ---
      steve.org.uk
Re: The Singleton design pattern and fork();
by Zaxo (Archbishop) on Feb 22, 2003 at 05:42 UTC
    Or do I have to brave the scary shared memory magic?

    Unfortunately, yes, at least in your design. Singleton is very uneasy with unadorned fork, since the new process obtains its own copy of environment and memory heap, and can do what it pleases with them without affecting the parent. That is at the heart of Unix process security.

    If you want to avoid shared memory, you can define a server which keeps authoritative data.

    After Compline,
    Zaxo

Re: The Singleton design pattern and fork();
by BrowserUk (Patriarch) on Feb 22, 2003 at 10:17 UTC

    If you have access to 5.8 you could always try threads. They certainly simplify the use of shared data with asynchronous processing.

    Some simplistic test code. I'm still getting to grips with Perls flavour of threading myself, so please take this with a very large pinch of salt.

    I'd really appreciate anyones critique of this code.

    #! perl -slw use strict; use threads; use threads::shared; package My::Singleton; my $count : shared = 0; sub new{ return __PACKAGE__; } sub incMe{ lock( $count ); ++$count } sub decMe{ lock( $count ); --$count } sub Me : lvalue { lock( $count ); $count } 1; package main; sub worker{ my ($ctl) = @_; my $me = threads->self; my $singleton = My::Singleton->new(); while($$ctl) { print $me, ' gets ', $singleton->Me(); sleep rand 3; print $me, ' increment to: ', $singleton->incMe(); sleep rand 3; print $me, ' decrements to: ', $singleton->decMe(); sleep rand 3; print $me, ' sets: ', $singleton->Me() = int rand 1000 if rand +() < 0.1; sleep rand 3; } print $me, ': Bye!'; return "$me: ended with " . $singleton->Me(); } my @flags : shared = (1) x 10; my @threads = threads->new( \&worker, \$_ ) for @flags; sleep 30; print 'Telling kids its time to go'; #!!! BAD IDEA.>> @flags = (0) x 10; << DO NOT USE!! [hossman]++ $_-- for @flags; # Better, *I think*. print 'Waiting...'; $_->self != threads->self and print($_->join) for threads->list;

    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.
      ... my @flags : shared = (1) x 10; my @threads = threads->new( \&worker, \$_ ) for @flags; sleep 30; print 'Telling kids its time to go'; @flags = (0) x 10; ...

      I know next to nothing about threads in perl, and i don't have 5.8 installed anywhere i can test this theory -- but threads or no threads i don't think that will do what you expect.

      When you generate a reference to each value in @flags, and pass that to each new worker thread, you get a reference to those values -- even if you then blow away the contents of @flags later. The workers should still have references to the orriginal values.

      Consider an unthreaded example...

      laptop:~> perl -w -Mstrict -l my @foo = (1) x 10; my $ref = \$foo[3]; @foo = (0) x 10; print "ref is now: " . $$ref; ref is now: 1

      Right? (Or is my lack of thread mojo causing me to missunderstand something major?)

        First, it does work under 5.8 and it doesn't work under 5.6.1.

        My best explaination for the difference in behaviour, and with luck Elian will browse past and set me straight if I have it wrong, is the use of the shared attribute. The clue, beyond my observations, is the documentation for threads::shared which states under the heading BUGS

        Does not support splice on arrays!

        Taking references to the elements of shared arrays and hashes does not autovivify the elements, and neither does slicing a shared array/hash over non-existent indices/keys autovivify the elements.

        The assumption I am making is that, in order to allow disperate threads to share variables, assignments to them 'update inplace' rather than allocating a new scalar and then swapping the pointers and letting the GC take care of the cleanup.

        That this is noted under bugs means the fact that it worked in the first place is basically happenstance and is not a behaviour I or anyone else should rely upon. As such I will be going back and changing it to

        $_-- for @flags;

        which would probably have been a better choice in the first place.

        Thankyou for pointing this out. You were especially observant if you have not yet made the transition to perls threads. ++

        Interestingly enough, the docs for threads::shared also state that the module will also work for Win32 pseudothreads and fork on that platform, which has possibilities if that is your platform.


        Examine what is said, not who speaks.
        1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
        2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
        3) Any sufficiently advanced technology is indistinguishable from magic.
        Arthur C. Clarke.
Re: The Singleton design pattern and fork();
by diotalevi (Canon) on Feb 22, 2003 at 07:12 UTC

    Other people told you to go learn shared memory and/or IPC. You could also re-architect and use a shared BerkeleyDB database which supports concurant access, transactions and locking. Or I'd use that because I usually have BDB already handy.


    Seeking Green geeks in Minnesota

       Thanks for the suggestion - looks like I'll go with DBI.

       (The original singleton I had planned was just a temporary measure until I wrote the DBI code to store this information for real).

      Steve
      ---
      steve.org.uk
Re: The Singleton design pattern and fork();
by mowgli (Friar) on Feb 22, 2003 at 10:35 UTC

    No - as others have pointed out, data is not being shared between the parent and child. However, you can use threads to get around that problem, and even if you don't want to or can't for some reason, shared memory is not *that* scary really (even though a thread-based programming model will probably still be much nicer).

    --
    mowgli