This simple example will show you how to use the DBM file when you want to be able to safely modify it in addition to just reading from it. As mentioned earlier, we are running in a multiprocess environment in which more than one process might attempt to write to the file at the same time. Therefore, we need to have a lock on the DBM file before we can access it, even when doing only a read operation—we want to make sure that the retrieved data is completely valid, which might not be the case if someone is writing to the same record at the time of our read. We are going to use the DB_File::Lock module from CPAN to perform the actual locking.

The simple script shown in Example 19-4 imports the O_RDWR and O_CREATsymbols from the Fcntl module, loads the DB_File::Lock module, and sends the HTTP header as usual.

Example 19-4. read_write_lock.pl

use strict;
use DB_File::Lock;
use Fcntl qw(O_RDWR O_CREAT);

my $r = shift;
$r->send_http_header("text/plain");

my $dbfile = "/tmp/foo.db";
tie my %dbm, 'DB_File::Lock', $dbfile, O_RDWR|O_CREAT,
    0600, $DB_HASH, 'write';
# assign a random value
$dbm{foo} = ('a'..'z')[int rand(26)];
untie %dbm;

# read the assigned value
tie %dbm, 'DB_File::Lock', $dbfile, O_RDWR|O_CREAT,
    0600, $DB_HASH, 'read';
my $foo = exists $dbm{foo} ? $dbm{foo} : 'undefined';
untie %dbm;

print "The value of foo: [$foo]";

The next step is to tie the existing /tmp/foo.db file, or create a new one if it doesn't already exist. Notice that the last argument for the tie is 'write', which tells DB_File::Lock to obtain an exclusive (write) lock before moving on. Once the exclusive lock is acquired and the DBM file is tied, the code assigns a random letter as a value and saves the change by calling untie( ), which unlocks the DBM and closes it. It's important to stress here that in our example the section of code between the calls to tie( ) and untie( ) is called a critical section, because while we are inside of it, no other process can read from or write to the DBM file. Therefore, it's important to keep it the execution time of this section as short as possible.

The next section is similar to the first one, but this time we ask for a shared (read) lock, as we only want to read the value from the DBM file. Once the value is read, it's printed. Since the letter was picked randomly, you will see something like this:

The value of foo: [d]

then this (when reloading again):

The value of foo: [z]

and so on.

Based on this example you can build more evolved code, and of course you may choose to use other locking wrapper modules, as discussed earlier.