# # Copyright (c) 2004-2007 - Consultas, PKG.fr # # This file is part of A2P. # # A2P is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # A2P is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with A2P; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # $Id: Status.pm 3 2007-10-18 16:20:19Z guillaume $ # use strict; use Test; use GDBM_File; use Storable qw( thaw ); require 'test/A2P/Defaults.pm' ; use warnings FATAL => qw(void); BEGIN { plan tests => 215, onfail => sub { exit(1) } } $| = 1 ; our $tie_file ; our $test_string ; our ( $freezed, $SERVICE_TMP, $SHMDIR, $ADVANCED_DEBUGGING, $LOCKID, $LOGFILENAME, $DEBUG_IN_FILE, %STATS ); our $Progname = 'TEST-Status' ; unlink $tie_file ; &info("Loading A2P::Status library"); ok require A2P::Status ; &info("Check default A2P::Status table"); ok join("*",&A2P::Status::INTIAL_STATUS), 'o*_*_*0*0*0*0*0*0*0*0*_*_' ; my $status ; my $cache_hashref = {} ; my $cache_hashref_2 = {} ; my %tied__hash = () ; my %h = () ; my %s = () ; my $do_next_test = 1 ; sub dump_hash { $h{This} = $cache_hashref ; map { print STDERR "CACHE: $_ -> ".$h{This}->{$_},($h{This}->{$_}=~/^HASH/? " -> (".keys(%{$h{This}->{$_}})." keys)":""),"\n" } keys(%{$h{This}}) ; map { print STDERR "CACHE: __SID__ -> $_ -> ".$h{This}->{__SID__}->{$_},"\n" } keys(%{$h{This}->{__SID__}}) ; $h{This} = \%tied__hash ; map { print STDERR "TIED : $_ -> ", ( $h{This}->{$_} =~ $freezed ? "Freezed storable object" : $h{This}->{$_}),"\n" } keys(%{$h{This}}) ; delete $h{This} ; } &info("Get a status object"); $status = new A2P::Status( $cache_hashref, \%tied__hash ); ok defined($status) ; ok ref($status) , 'A2P::Status' ; &info("Check cache"); ok $cache_hashref->{'noname'} , $status ; ok keys(%{$cache_hashref}) , 2 ; sub check_status_defaults { #&dump_hash ; my $self = shift ; my $job = shift ; &info("Checking Status object defaults of " . $job ); ok $self->{JOB}, $job ; ok $self->is_job, $job ; ok $self->{LOCKID}, $LOCKID ; ok $self->{DAY}, 19700101 ; &info("Checked"); } &check_status_defaults($status,'noname'); ok $status->{AFP}, 'noname' ; ok $status->get_sid_index, 0 ; &info("Insert status in cache"); ok $status->is_job('test'), 'test' ; ok $status->cached ; &info("Check cache"); ok $cache_hashref->{'test'} , $status ; ok keys(%{$cache_hashref}) , 3 ; #&dump_hash ; delete $cache_hashref->{'noname'} ; delete $cache_hashref->{'test'} ; &info("Try tied cache"); ok defined(tie( %tied__hash, 'GDBM_File', $tie_file, GDBM_WRCREAT, 0660)) ; $cache_hashref->{__GDBM_WRITER__} = 1 ; &info("Insert status in tied hash"); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-1' ); ok $cache_hashref->{'TEST-UNIT-1'} , $status ; &check_status_defaults($status,'TEST-UNIT-1'); ok $status->{AFP}, 'TEST-UNIT-1' ; ok $status->get_sid_index, 0 ; &info("Set jobname, a private scalar and save to tied hash"); ok $status->is_job('TEST-UNIT-2') ; ok $status->cached ; ok $status->{"TESTUNIT-$$"} = '__PRIVATE__' ; ok $status->{"TESTUNIT-$$"} , '__PRIVATE__' ; &info("Control 'is_tied' returns the same reference avec being tied"); ok $status->is_tied, 0 ; # Null when not in tied hash ok $status->save_tied ; &info("Status ref is different as re-read from tied hash"); my $tied = $status->is_tied ; ok $tied != $status ; ok ref($status) , 'A2P::Status' ; ok ref($tied) , 'A2P::Status' ; ok $status->get_sid_index, $tied->get_sid_index ; ok $cache_hashref->{'TEST-UNIT-2'} , $status ; ok $cache_hashref->{'TEST-UNIT-2'} != $tied ; &info("Close tie"); untie %tied__hash ; &info("Reopen as reader and get one in tied hash"); ok defined(tie( %tied__hash, 'GDBM_File', $tie_file, GDBM_READER, 0660)) ; $cache_hashref->{__GDBM_WRITER__} = 0 ; ok defined($tied__hash{'TEST-UNIT-2'}) ; my $intied = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-2' ); &info("Check status is inserted in cache automatically"); ok defined($intied) and ref($intied) =~ /^A2P::Status$/ ; ok defined($cache_hashref->{'TEST-UNIT-2'}) ; #&dump_hash ; &info("Control the retrieved jobname"); ok defined($intied->{JOB}) ; ok $intied->is_job() ; ok $intied->is_job(), 'TEST-UNIT-2' ; &info("Control private inserted value is there"); ok $intied->{"TESTUNIT-$$"} , '__PRIVATE__' ; #&dump_hash ; untie %tied__hash ; &info("Reopen as writer and get new status"); ok defined(tie( %tied__hash, 'GDBM_File', $tie_file, GDBM_WRCREAT, 0660)) ; $cache_hashref->{__GDBM_WRITER__} = 1 ; $intied = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-3' ); ok defined($intied) and ref($intied) =~ /^A2P::Status$/ ; ok $intied->get_revision(), 0 ; &info("Test API used by tied_update_status in A2P::JobStatus"); &info("Check cache_timer call"); ok $intied->cache_timer ; &info("Check step_status call"); ok $intied->step_status ; ok $intied->step_status(-1), 0 ; ok $intied->step_status(0), 'o'; ok $intied->step_status(12), '_'; ok $intied->{STATE}->[12], '_' ; ok ! $intied->step_status(20); ok $intied->step_status(5), '_' ; # if char '0' return '_' ok $intied->step_status(5); # Still true by default ok $intied->step_status(5,'0'), '.'; # Can't set '0' but '.' as default ok $intied->step_status(12,'X'), 'X' ; ok $intied->step_status(12), 'X'; ok $intied->{STATE}->[12], 'X' ; ok $intied->{STEP}, 12 ; #&dump_hash ; &info("Check infos API"); ok $intied->infos(), 1 ; ok $intied->infos(1), 0 ; ok $intied->infos("AA"), 0 ; ok $intied->infos( ( TEST => 111 ) ), 0 ; ok $intied->infos( { TEST => 111 } ), 1 ; ok $intied->infos( [ TEST => 111 ] ), 0 ; ok $intied->{TEST}, 111 ; my @test = 'TEST' ; &info("Try a call with a bad hash (it generates at least a warning)"); ok $intied->infos( { @test } ), 1 ; ok $intied->{TEST}, "" ; ok $intied->infos({ THIS => "TESTUNIT" }) ; ok $intied->{THIS}, 'TESTUNIT' ; ok $intied->infos( { THIS => " " }) ; ok $intied->{THIS}, '' ; ok $intied->infos( { JOBID => 'UNIT-TEST' } ), 1 ; ok $intied->{JOBID}, 'UNIT-TEST' ; ok $intied->cached ; ok defined($cache_hashref->{'TEST-UNIT-3'}) ; ok $cache_hashref->{'TEST-UNIT-3'}, $intied ; ok defined($cache_hashref->{'UNIT-TEST'}) ; ok $cache_hashref->{'UNIT-TEST'}, $intied ; &info("Check current_in_tie_sid_key API"); ok $intied->get_sid_index, 0 ; ok $intied->current_in_tie_sid_key , 'TEST-UNIT-3' ; &info("Check status API"); ok $intied->infos( { STATUS => "done" } ) ; ok $intied->{STATUS}, 'done' ; ok $intied->status, 'DONE' ; ok $intied->{STATUS}, 'DONE' ; &info("Check save_tied call"); $intied = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-4' ); ok $intied->get_revision(), 0 ; ok $intied->save_tied(), $intied ; ok $intied->get_revision(), 1 ; ok defined($tied__hash{'TEST-UNIT-4'}) ; $cache_hashref->{__GDBM_WRITER__} = 0 ; &info("Must not save_tied and return 0"); ok $intied->save_tied(), 0 ; ok $intied->get_revision(), 2 ; $cache_hashref->{__GDBM_WRITER__} = 1 ; &info("Check about revision API"); ok $intied->get_revision(), 2 ; ok $intied->{REV}, 2 ; ok $intied->set_revision(), 3 ; ok $intied->get_revision(), 3 ; ok $intied->{REV}, 3 ; ok $intied->set_revision(), 4 ; ok $intied->{REV}, 4 ; &info("Check current_in_tie_sid_key API"); ok $intied->current_in_tie_sid_key , 'TEST-UNIT-4' ; &info("Simulating SID updates from another thread:"); $LOCKID = "SIMUL-1" ; $0 = "TEST-Status-SM" , A2P::Syslog::SetLogger(0); &info("1. First call initialize the status object"); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-5' ); ok $status->step_status ; ok $status->infos ; ok $status->cached ; ok $s{timer1} = $status->cache_timer ; $s{timer2} = $status->timer ; ok $s{timer1} , $s{timer2} ; ok $status->save_tied , $status ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok ! exists($cache_hashref->{'SIMULATION-1'}) ; ok $status->get_revision , 1 ; ok $status->get_sid_index , 0 ; ok $status->get_sid_key , 'TEST-UNIT-5' ; ok $status->get_sid_version_key , 'TEST-UNIT-5_rev' ; $s{version} = &thaw($tied__hash{'TEST-UNIT-5_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 1 ; ok $s{version}->[1] , "SIMUL-1" ; ok $s{version}->[2] , $s{timer2} ; undef $status ; ok defined($tied__hash{'TEST-UNIT-5'}) ; ok defined($tied__hash{'TEST-UNIT-5_rev'}) ; ok $tied__hash{'SIMULATION-1'} , undef ; #&dump_hash ; &info("2. Second call initialize the status itself and a private value"); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-5' ); ok $status->step_status(1,'.') , '.'; ok $status->infos({ STATUS => 'WAITING', FILE => 'TEST.test-unit-5' }); ok $status->cached ; ok $status->cache_timer ; ok $status->save_tied , $status ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok ! exists($cache_hashref->{'SIMULATION-1'}) ; undef $status ; ok defined($tied__hash{'TEST-UNIT-5'}) ; ok defined($tied__hash{'TEST-UNIT-5_rev'}) ; $s{version} = &thaw($tied__hash{'TEST-UNIT-5_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 2 ; ok $tied__hash{'SIMULATION-1'} , undef ; &info("3. Third call initialize the JID"); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-5' ); ok $status->step_status(1,'o') , 'o'; ok $status->step_status(1,'o') , 'o'; ok $status->step_status(1,'-') , 'o'; ok $status->infos({ JID => 'SIMULATION-1', STATUS => 'STARTING', FILE => 'TEST/test-unit-5' }); ok $status->cached ; ok $status->cache_timer ; ok $status->save_tied , $status ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok $cache_hashref->{'SIMULATION-1'}, $status ; undef $status ; ok defined($tied__hash{'TEST-UNIT-5'}) ; ok defined($tied__hash{'TEST-UNIT-5_rev'}) ; $s{version} = &thaw($tied__hash{'TEST-UNIT-5_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 3 ; ok $tied__hash{'SIMULATION-1'} , 'TEST-UNIT-5' ; $cache_hashref = { __GDBM_WRITER__ => 1 } ; #&dump_hash ; &info("4. Next update from converter using another cache"); $0 = "TEST-Status-CO" , A2P::Syslog::SetLogger(0); $cache_hashref_2->{__GDBM_WRITER__} = 1 ; $status = new A2P::Status( $cache_hashref_2, \%tied__hash, 'TEST-UNIT-5' ); ok $status->step_status(2,'.') , '.'; ok $status->infos({ JID => 'SIMULATION-1' }); ok $status->cached ; ok $status->cache_timer ; ok $status->save_tied , $status ; ok $cache_hashref_2->{'TEST-UNIT-5'} , $status ; ok $cache_hashref_2->{'SIMULATION-1'}, $status ; ok $cache_hashref_2->{'__SID__'}->{3}, undef ; undef $status ; ok defined($tied__hash{'TEST-UNIT-5'}) ; ok defined($tied__hash{'TEST-UNIT-5_rev'}) ; $s{version} = &thaw($tied__hash{'TEST-UNIT-5_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 4 ; ok $tied__hash{'SIMULATION-1'} , 'TEST-UNIT-5' ; &info("5. Clear the cache to simulate check from a2p-status as reader"); $cache_hashref = { __GDBM_WRITER__ => 0 } ; #&dump_hash ; &info("6. In the same time a2p-status get the status and update it in DB"); $0 = "TEST-Status-DB" , A2P::Syslog::SetLogger(0); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-5' ); &info("Check current_in_tie_sid_key API"); ok $status->current_in_tie_sid_key , 'TEST-UNIT-5' ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok $cache_hashref->{'SIMULATION-1'}, $status ; ok $status->get_sid_index , 0 ; &info("7. Here a sync with DB result in a change of SID"); ok A2P::Status::_found_sid() , 0 ; ok $status->{DB}->{SID} , undef ; $status->{PREVIOUS_SID} = 'TEST-UNIT-5' ; $status->{SID} = $status->{DB}->{SID} = 3 ; ok $status->get_revision, 4 ; $status->{DB}->{REV} = $status->get_revision ; ok $status->get_sid_index , 3 ; $cache_hashref->{'__SID__'}->{$status->get_sid_index} = $status ; ok A2P::Status::_found_sid(3), 3 ; ok A2P::Status::_found_sid() , 4 ; ok $status->cache_timer ; #&dump_hash ; &info("Check current_in_tie_sid_key API"); ok $status->current_in_tie_sid_key , 'TEST-UNIT-5' ; &info("8. Check we can't update status tied hash"); ok $status->get_revision, 4 ; ok $status->save_tied , 0 ; ok $status->get_revision, 5 ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok $cache_hashref->{'SIMULATION-1'}, $status ; ok $cache_hashref->{'__SID__'}->{3}, $status ; ok defined($tied__hash{'TEST-UNIT-5'}) ; ok defined($tied__hash{'TEST-UNIT-5_rev'}) ; $s{version} = &thaw($tied__hash{'TEST-UNIT-5_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 4 ; ok ! exists($tied__hash{'_SID_3'}) ; ok ! exists($tied__hash{'_SID_3_rev'}) ; ok $tied__hash{'SIMULATION-1'} , 'TEST-UNIT-5' ; #&dump_hash ; &info("9. Retry to update tied hash"); $cache_hashref->{__GDBM_WRITER__} = 1 ; undef $status ; $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST-UNIT-5' ); ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok $cache_hashref->{'SIMULATION-1'}, $status ; ok $status->get_sid_index , 3 ; ok $cache_hashref->{'__SID__'}->{3}, $status ; &info("Here sync_with_db simulates a change of SID with PREVIOUS_SID"); ok $status->{SID} , 3 ; ok $status->get_sid_index , 3 ; ok $status->{PREVIOUS_SID} , 'TEST-UNIT-5' ; ok $status->{DB}->{SID} , 3 ; ok $status->cache_timer ; &info("10. Check we have updated status tied hash"); ok $status->save_tied , $status ; # Only deleted after save_tied in sync_with_db delete $tied__hash{'TEST-UNIT-5_rev'} ; ok $status->get_revision, 6 ; ok $cache_hashref->{'TEST-UNIT-5'} , $status ; ok $cache_hashref->{'SIMULATION-1'}, $status ; ok $cache_hashref->{'__SID__'}->{3}, $status ; ok $tied__hash{'TEST-UNIT-5'} , '3' ; ok ! exists($tied__hash{'TEST-UNIT-5_rev'}) ; ok defined($tied__hash{'_SID_3'}) ; ok defined($tied__hash{'_SID_3_rev'}) ; $s{version} = &thaw($tied__hash{'_SID_3_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 6 ; ok $tied__hash{'SIMULATION-1'} , 'TEST-UNIT-5' ; &info("Check current_in_tie_sid_key API"); ok $status->current_in_tie_sid_key , '_SID_3' ; undef $status ; &info("11. Following check as reader are done with new SID key"); $cache_hashref->{__GDBM_WRITER__} = 0 ; undef $status ; $status = new A2P::Status( $cache_hashref, \%tied__hash, '_SID_3' ); ok $status->get_revision, 6 ; ok $status->get_sid_index , 3 ; $cache_hashref->{'__SID__'}->{$status->get_sid_index} = $status ; #&dump_hash ; &info("Check current_in_tie_sid_key API"); ok $status->current_in_tie_sid_key , '_SID_3' ; &info("12. Use the other cache and control SID is not troubled"); $0 = "TEST-Status-CO" , A2P::Syslog::SetLogger(0); $status = new A2P::Status( $cache_hashref_2, \%tied__hash, 'TEST-UNIT-5' ); &info("Check current_in_tie_sid_key API"); ok $status->current_in_tie_sid_key , '_SID_3' ; ok $status->step_status(2,'o') , 'o'; ok $status->infos ; ok $status->cached ; ok $status->cache_timer ; #&dump_hash ; ok $status->save_tied , $status ; ok $status->get_revision, 7 ; ok $cache_hashref_2->{'TEST-UNIT-5'} , $status ; ok $cache_hashref_2->{'SIMULATION-1'}, $status ; ok $cache_hashref_2->{'__SID__'}->{3}, $status ; ok ! exists($tied__hash{'TEST-UNIT-5_rev'}) ; ok defined($tied__hash{'_SID_3'}) ; ok defined($tied__hash{'_SID_3_rev'}) ; ok defined($tied__hash{'_SID_3_rev'}) ; $s{version} = &thaw($tied__hash{'_SID_3_rev'}) ; ok @{$s{version}} , 3 ; ok $s{version}->[0] , 7 ; ok $tied__hash{'SIMULATION-1'} , 'TEST-UNIT-5' ; ok $tied__hash{'TEST-UNIT-5'} , '3' ; undef $status ; &info("Check remove_from_cache call"); $cache_hashref->{__GDBM_WRITER__} = 1 ; $intied = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST_UNIT_6' ); ok $intied->save_tied(), $intied ; ok defined($tied__hash{'TEST_UNIT_6'}) ; ok $cache_hashref->{'TEST_UNIT_6'}, $intied ; $intied->remove_from_cache ; ok defined($tied__hash{'TEST_UNIT_6'}) ; ok $cache_hashref->{'TEST_UNIT_6'}, undef ; &info("Check cache/timer info APIs"); ok $cache_hashref->{__GDBM_WRITER__} ; $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST_UNIT_7' ); ok $status->cached ; ok exists($cache_hashref->{'TEST_UNIT_7'}) ; ok $cache_hashref->{'TEST_UNIT_7'}, $status ; ok exists($cache_hashref->{'TEST_UNIT_7'}->{TIMER}) ; ok $cache_hashref->{'TEST_UNIT_7'}->{TIMER} ; ok $cache_hashref->{'TEST_UNIT_7'}->timer ; my $timer = $cache_hashref->{'TEST_UNIT_7'}->{TIMER} ; ok $cache_hashref->{'TEST_UNIT_7'}->timer, $timer ; &info("Check compared_with_one_from_tiehash API"); ok $status->get_revision, 0 ; ok $status->save_tied(), $status ; ok $status->get_revision, 1 ; ok defined($tied__hash{'TEST_UNIT_7'}) ; my $dirty = $status->clone ; ok $status->save_tied(), $status ; ok $status->get_revision, 2 ; ok $dirty->get_revision, 1 ; # Dirty cache entry ok $status->compared_with_one_from_tiehash, $status ; my $updated = $dirty->compared_with_one_from_tiehash ; ok $updated != $dirty ; ok $updated->get_revision, 2 ; $updated->cached ; ok $cache_hashref->{'TEST_UNIT_7'}, $updated ; ok $dirty->get_revision, 1 ; ok $dirty->set_revision, 2 ; ok $dirty->compared_with_one_from_tiehash, $dirty ; ok $dirty->set_revision, 3 ; ok $dirty->compared_with_one_from_tiehash, $dirty ; ok $cache_hashref->{'TEST_UNIT_7'} != $dirty ; $do_next_test = 0 ; if ($do_next_test) { # This long test should normally be disabled &info("Check remove_from_tied_hash API timing"); $status = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST_UNIT_8' ); my $ostatus = new A2P::Status( $cache_hashref, \%tied__hash, 'TEST_UNIT_9' ); ok $status->save_tied(), $status ; ok $ostatus->save_tied(), $ostatus ; ok defined($tied__hash{'TEST_UNIT_8'}) ; my $i = 0 ; while ( $i++ < 10000 ) { #&dump_hash if ( $i == 10 ); $status->is_job('TEST_UNIT_8_ref_'.$i); $status->save_tied(); $ostatus->is_job('TEST_UNIT_9_ref_'.$i); $ostatus->save_tied(); } $status->set_sid_index(500); $status->save_tied ; $ostatus->set_sid_index(5000); $ostatus->save_tied ; my ( $size, $maxsize ) = ( -s $tie_file, 3000000 ); &info("Memory file size = $size bytes"); $size > $maxsize ? ok $size, $maxsize : ok 1 ; my ( $timing, $maxtiming ) = ( $STATS{'TIMING-SAVE-TIED-MAX'}, 20000 ); &info("Max save to tied timing = $timing"); $timing > $maxtiming ? ok $timing, $maxtiming : ok 1 ; $status->remove_from_cache ; $status->remove_from_tied_hash(); ( $timing, $maxtiming ) = ( $STATS{'TIMING-REMOVE-FROM-TIED-MAX'}, 700000 ); &info("Max remove timing = $timing"); $timing > $maxtiming ? ok $timing, $maxtiming : ok 1 ; my $tiedhash = tied( %tied__hash ) ; ok ref($tiedhash) , 'GDBM_File' ; &info("Shrinking memory file..."); my $shrinked = -s $tie_file ? $tiedhash->reorganize() : 0 ; undef $tiedhash ; (defined($shrinked) and $shrinked >= 0) ? ok $shrinked, 0 : ok 0 ; ( $size, $maxsize ) = ( -s $tie_file, 1200000 ); &info("Shrinked memory file size = $size bytes"); $size > $maxsize ? ok $size, $maxsize : ok 1 ; } # Clean #&dump_hash ; untie %tied__hash ; unlink $tie_file ; exit(0) ;