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

I have an MP3 server (ala this node ) and I want the option of playing each MP3 in a linear fashion (rather than a random fashion).

I thought Cache::SharedMemoryCache would be the best and fastest option, and it does work, except when the user presses the next (or previous) button on their MP3 player (tested with xmms).

When the next (or previous) button is pressed, the child process is killed, and a new process is forked, and the counter is reset to zero.

My expectation is (was) that the SharedMemoryCache would actually share the cache between different processes, meaning each time the user clicks the next button it would go get the value out of the cache and use it.

Its quite possible I've incorrectly implemented it, however it looked really simple in the perdoc.

I know there are other methods of doing this, such as sticking the counter in a flat file or a db or something, but I figured using Cache::SharedMemoryCache would be the fastest (and easiest) access method.

At a guess i'm thinking each child spawns its own SharedMemoryCache object, meaning that it cant actually get to any previously initated cache - could i possibly be correct? (and if so, is there an alternate method to use it?) damn i hope so.

Alpha code - beware:

#!/usr/bin/perl -w use strict; use IO::Socket; use MIME::Base64; use DBI; use Data::Dumper; use Storable qw/freeze thaw/; use Cache::SharedMemoryCache; my $songcache = 'songnumber'; my $cache = new Cache::SharedMemoryCache( { 'namespace' => $songcache, 'default_expires_in' => 60 +0 } ); #get the port to bind to or default to 8000 my $port = $ARGV[0] || 8000; #ignore child processes to prevent zombies $SIG{CHLD} = 'IGNORE'; #create the listen socket my $listen_socket = IO::Socket::INET->new(LocalPort => $port, Listen => 10, Proto => 'tcp', Reuse => 1); open (PIDFILE, '>'.$0.'.pid'); print PIDFILE $$; close PIDFILE; #make sure we are bound to the port die "Cant't create a listening socket: $@" unless $listen_socket; warn "Server ready. Waiting for connections ... \n"; my (@auth, @ary, $buf, %kids); #wait for connections at the accept call while (my $connection = $listen_socket->accept) { my $child; # perform the fork or exit die "Can't fork: $!" unless defined ($child = fork()); if ($child == 0) { #i'm the child! $connection->recv($buf, 1024); @ary = split(/0d0a/,unpack("H*",$buf) ); foreach (@ary){ my $line = pack("H*", $_); @auth = split(/ /,$line ) if ($line =~ /^Auth/); } #close the child's listen socket, we dont need it. $listen_socket->close; #call the main child rountine play_songs($connection, $cache, \@auth); #if the child returns, then just exit; undef $kids{$child}; exit 0; } else { #i'm the parent! $kids{$child} = 1; #who connected? warn "Connecton recieved ... ",$connection->peerhost,"\n"; #close the connection, the parent has already passed # it off to a child. $connection->close(); } #go back and listen for the next connection! } sub play_songs { my $socket = shift; my $cache = shift; my $ary = shift; my @songs; #get all the possible songs if ($#{$ary} == -1) { #get default playlist local*PLAYLIST; open PLAYLIST, "playlist.m3u" or die; @songs = <PLAYLIST>; close PLAYLIST; chomp @songs; } else { my @params = split(/\:/,decode_base64(@{$ary}[$#{$ary}]) ); @songs = &get_db_playlist(NAME=>$params[0], PLAYLIST=>$params[ +1]); } my $i; $cache->set($songcache, $i); #seed the rand number generator srand(time / $$); #loop forever (oruntil the client closes the socket) while() { #print the HTTP header. The only thing really necessary # is the first line and the trailing "\n\n" # depen +ding on your client (like xmms) you can also # send song title etc. print $socket "HTTP/1.0 200 OK\n"; print $socket "Content-Type: audio/x-mp3stream\n"; print $socket "Cache-Control: no-cache \n"; print $socket "Pragma: no-cache \n"; print $socket "Connection: close \n"; print $socket "x-audiocast-name: My MP3 Server\n\n"; print STDERR "1) i=$i songs: $#songs cache: ".$cache->get($songcache). +" PID: ".$$."\n"; #get a random song from your playlist #my $song = $songs[ rand @songs ]; $i = $cache->get($songcache); if (defined $i) { $i++; if ($i > $#songs) {$i=0}; $cache->set($songcache, $i); print STDERR "2) i=$i songs: $#songs cache: ".$cache->get($songcache). +" PID: ".$$."\n"; } else { $i = 0; $cache->set($songcache, $i); } print STDERR Dumper $cache; my $song = $songs[$i]; #what song are we playing warn( "play song: $song\n"); #open the song, or continue to try another one open (SONG, $song) || next; binmode(SONG); #for windows users my $read_status = 1; my $print_status = 1; my $chunk; # This parts print the binary to the socket # as fast as it can. The buffering will # take place on the client side (it blocks when full) # because this is *not* non-blocking IO # #the read will return 0 if it has reached eof # #the print will return undef if it fails # (ie the client stopped listening) # while( $read_status && $print_status ) { $read_status = read (SONG, $chunk, 1024); if( defined $chunk && defined $read_status) { $print_status = print $socket $chunk; } undef $chunk; } print STDERR "3) i=$i songs: $#songs cache: ".$cache->get($songcache). +" PID: ".$$."\n"; close SONG; unless( defined $print_status ) { $socket->close(); exit(0); } } } sub get_db_playlist { my %params = @_; my ($dbh, $sth, @ary); $dbh = DBI->connect('DBI:Pg:dbname=infomgr',uname , password, { Ra +iseError => 1, AutoCommit => 0 }) || die "could not connect to database: ".$dbh->errstr;; $sth = $dbh->prepare("SELECT a.playlist from playlist a, users b w +here b.name = ? and a.playlist_name = ?"); $sth->execute($params{NAME},$params{PLAYLIST}); @ary = $sth->fetchrow_array; $sth->finish; $dbh->disconnect; if ($dbh->errstr) {warn "Error getting playlist: ".$dbh->errstr }; my $retval = thaw(pack("H*", @ary) ); return @{$retval}; }

Edit Petruchio Thu Feb 7 07:51:18 UTC 2002 - Added READMORE tag

Replies are listed 'Best First'.
Re: SharedMemoryCache: How does it really work?
by perrin (Chancellor) on Feb 07, 2002 at 17:06 UTC
    Cache::FileCache would probably be easier to use and is often faster than Cache::SharedMemoryCache. However, both should work. You probably have a problem in your code somewhere that is preventing it from doing what you expect. Do some debugging.
Re: SharedMemoryCache: How does it really work?
by Ryszard (Priest) on Feb 11, 2002 at 00:18 UTC
    I've been asking around some of my friends on the way SharedMemoryCache works, and one repled with the following:

    The way shared memory works is that all CODE segments are loaded into SHM, not DATA which usually goes on the stack. By sharing CODE segments, your programs can reuse parts of the memory store where there's no change in the code. If you wanted to store DATA in SHM, you'd need to do something custom to UNIX and need gigs of RAM.

    Either that or write something to alloc all the memory ahead of time....

    It sort of bummed me out a bit, however I'm now taking the opportunity to write a persistant cache module.

    Yup, I know there is already a Cache::SharedFileCache, however I figure I might use this opportunity to hone my OOP skills (which arent very good).

    My module wont be very complicated, and will use a RDBMS for the store, and only have a couple of methods, namely memorise, new, and remember.

    Comments welcome..

A reply falls below the community's threshold of quality. You may see it by logging in.