hotti: Problem mit Tie::Scalar

hi,

kann den Fehler einfach nicht finden. Untenstehendes kleines Modul bindet ein Scalar an eine Klasse. Beim Aufruf in der main wird die Variable hochgezählt, aber nach der 26 gehts bei 2 wieder von vorn los. Eigenartigerweise klappt die Erhöhung, wenn eine Schleife drumgelegt wird.

Idee's ???

Hotti

  
  
package Number;  
  
use strict;  
use Fcntl qw(:DEFAULT :flock);  
use Tie::Scalar;  
use vars qw(@ISA);  
@ISA = qw(Tie::StdScalar);  
use warnings;  
  
my $FH; # File Handle  
  
# Konstruktor  
sub TIESCALAR{  
	my $class = shift;  
	# Repository File, Schreibberechtigung sicherstellen!  
	my $file =  shift || '/tmp/number.bin';  
	  
	sysopen($FH, $file, O_RDWR|O_CREAT) or return;  
	flock $FH, LOCK_EX;  
	binmode $FH;  
	my $bin = undef;  
	read $FH, $bin, 4;  
	my $nr = unpack("N", $bin) || 1;  
	return bless \$nr, $class;  
}  
  
# Schreibe die Nummer zurück in die Repository  
sub DESTROY{  
	my $self = shift;  
	truncate $FH, 0;  
	seek $FH, 0, 0;  
	print $FH pack "N", $$self;  
	close $FH;  
	#print "\nDESTROY\n";  
}  
###########################################################################  
1;  
###########################################################################  
  
package main;  
tie(my $c, 'Number') or die $!;  
$c++;  
print $c;  

  1. kann den Fehler einfach nicht finden. Untenstehendes kleines Modul bindet ein Scalar an eine Klasse. Beim Aufruf in der main wird die Variable hochgezählt, aber nach der 26 gehts bei 2 wieder von vorn los. Eigenartigerweise klappt die Erhöhung, wenn eine Schleife drumgelegt wird.

    Idee's ???

    Kann ich nicht nachvollziehen. Ich bin jetzt bei 44..

    Allerdings ist der Ansatz nicht wirklich tauglich, du kannst immer nur einen Skalar binden, da du nur einen Filehandle hast, der zweite wird eine Warnung erzeugen.

    Struppi.

    1. kann den Fehler einfach nicht finden. Untenstehendes kleines Modul bindet ein Scalar an eine Klasse. Beim Aufruf in der main wird die Variable hochgezählt, aber nach der 26 gehts bei 2 wieder von vorn los. Eigenartigerweise klappt die Erhöhung, wenn eine Schleife drumgelegt wird.

      Idee's ???

      Kann ich nicht nachvollziehen. Ich bin jetzt bei 44..

      Danke ;)

      Es ist total verwirrend um nicht zu sagen zum Verzweifeln. Ich habe jetzt im Modul mal die pack/unpack-Schablone auf "V" gesetzt und es funktioniert. Tatsächlich habe ich hier eine little-Endian-Architektur (Intel), lt. perldoc -f pack:

      print join(" ", map { sprintf "%#02x", $_ } unpack("C*",pack("L",0x12345678))), "\n";
      0x12 0x34 0x56 0x78     # big-endian
      0x78 0x56 0x34 0x12     # little-endian

      Großes Arber: Die pack/unpack-Schablonen sind plattformunabhängig, auch wenn die 4 Bytes in Dateien geschrieben werden, bisher konnte ich die stets exact wiederherstellen, egal ob mit "N" oder "V" (und auch mit aus Tie::Hash abgeleiteten Klassen). Ich vermute einen Bug in Tie::Scalar oder Tie::StdScalar und Du hast sicher eine neuere Perl-Version/neuere Tie-Module und das Problem ist aus der Welt (ich gucke dann mal auf CPAN die Bug-Reports durch).

      Allerdings ist der Ansatz nicht wirklich tauglich, du kannst immer nur einen Skalar binden, da du nur einen Filehandle hast, der zweite wird eine Warnung erzeugen.

      Schlimmer: Es wird sogar einen dead-Lock ergeben, wenn dieselbe Datei in _einem_ Prozess verwendet wird. Der Ansatz ist überarbeitungsbedürftig, ja, hast Recht. Vielleicht sollte ich auch anstelle einer gebundenen Variablen einfach nur das Objekt zurückgeben, wobei das Problem mit dem möglichen deadLock noch anderweitig beseitigt werden muss. Ich mach dann erstmal Kaffee ;)

      Horst Wirrsign

      --
      Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
  2. hallo zu später Stunde,

    den Fehler konnte ich soweit eingrenzen:

    sysopen($FH, $file, O_RDWR|O_CREAT);

    kürzt die Datei von 4 auf 3 Byte, wenn 4 byte drinstehen mit dem Wert 0x1A (dezimal 26).

    Verwende ich
    sysopen($FH, $file, O_RDONLY);
    wird die Datei nicht vermurkst; wäre ja noch schöner, aber ich will ja auch schreiben und ggf. anlegen.

    Falls jemand noch eine Idee hat:
    This is perl, v5.6.1 built for MSWin32-x86-multi-thread
    (with 1 registered patch, see perl -V for more detail)

    Copyright 1987-2001, Larry Wall

    Binary build 638 provided by ActiveState Corp. http://www.ActiveState.com
    ActiveState is a division of Sophos.
    Built Apr 13 2004 19:24:21

    Perl may be copied only under the terms of either the Artistic License or the
    GNU General Public License, which may be found in the Perl 5 source kit.

    Complete documentation for Perl, including FAQ lists, should be found on
    this system using man perl' or perldoc perl'.  If you have access to the
    Internet, point your browser at http://www.perl.com/, the Perl Home Page.

    Danke für Hinweise,
    Horst Ratlos

    1. Moin Moin!

      Falls jemand noch eine Idee hat:
      This is perl, v5.6.1 built

      Warnung 1

      for MSWin32-x86-multi-thread

      Warnung 2

      (with 1 registered patch, see perl -V for more detail)

      Warnung 3

      Copyright 1987-2001, Larry Wall

      Binary build 638 provided by ActiveState Corp. http://www.ActiveState.com

      Warnung 4

      ActiveState is a division of Sophos.
      Built Apr 13 2004 19:24:21

      Warnung 5

      Danke für Hinweise,

      Aktuell ist Perl 5.12. Ein paar fertige Binaries für Win32 bekommst Du unter http://strawberryperl.com/

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
      1. Moin Moin!

        Aktuell ist Perl 5.12. Ein paar fertige Binaries für Win32 bekommst Du unter http://strawberryperl.com/

        Freilich ist ein Upgrade abzuwägen. Nochn paar Worte zu diesem selten dämlichen Bug in sysopen(): Das Problem tritt auf, wenn 4 byte mit dem Wert 0x1A (26) für sich allein oder am Ende einer Datei stehen, die mit sysopen(,, O_RDWR|O_CREAT) geöffnet wird. Es tritt nicht auf, wenn in der Datei weitere Bytes folgen. Daraus lässt sich ein einfacher Workaround ableiten, den ich bereits gestern nachmittag realisiert habe.

        Wenn Du experimentierfreudig bist (ja), teste das mal mit einer neueren Perlversion, auf das Ergebnis bin ich gespannt.

        Schönen Sonntag,
        Hotti

        1. Moin Moin!

          Wenn Du experimentierfreudig bist (ja), teste das mal mit einer neueren Perlversion, auf das Ergebnis bin ich gespannt.

          Schreib einen "self-contained" Test, der eine entsprechende Datei anlegt, mit dem angeblichen Bug demoliert und anschließend überprüft, dass die Datei wirklich demoliert wurde. Den lasse ich dann gerne auf zwei oder drei verschiedenen Systemen laufen.

          Alexander

          --
          Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
          1. Moin, moin,

            Schreib einen "self-contained" Test, der eine entsprechende Datei anlegt, mit dem angeblichen Bug demoliert und anschließend überprüft, dass die Datei wirklich demoliert wurde. Den lasse ich dann gerne auf zwei oder drei verschiedenen Systemen laufen.

            Gerne, steht unten.

            Bis dann,
            Hotti

              
            #!/usr/bin/perl  
              
            ###########################################################################  
            # teste Verhalten sysopen() mit 0x1A  
              
            # VARs  
            my $file = 'testfile';  
            ###########################################################################  
            use strict;  
            use Fcntl qw(:DEFAULT :flock);  
              
            # Schreibe genau 4 Bytes in die Datei  
            sysopen(OUT, $file, O_RDWR|O_CREAT) or die $!;  
            seek OUT, 0, 0;  
            truncate OUT, 0;  
            binmode OUT;  
            print OUT pack "N", 0x1A;  
            close OUT;  
              
            # Testen der Dateilänge  
            my @stats = stat($file);  
            printf qq(Dateilaenge nach Schreiben: %u Byte\n), $stats[7];  
              
            # Datei nur öffnen mit sysopen(), nix weiter  
            sysopen(OUT, $file, O_RDWR|O_CREAT) or die $!;  
            close OUT;  
              
            # Testen der Dateilänge  
            @stats = stat($file);  
            printf qq(Dateilaenge nach sysopen: %u Byte\n), $stats[7];  
            
            

            __END__
            Dateilaenge nach Schreiben: 4 Byte
            Dateilaenge nach sysopen: 3 Byte

            1. Moin Moin!

              Auf Linux 2.6.35.4 (Slackware 13.1 32 bit):

              perl -v

              This is perl, v5.10.0 built for i486-linux-thread-multi

              Copyright 1987-2007, Larry Wall

              Perl may be copied only under the terms of either the Artistic License or the
              GNU General Public License, which may be found in the Perl 5 source kit.

              Complete documentation for Perl, including FAQ lists, should be found on
              this system using "man perl" or "perldoc perl".  If you have access to the
              Internet, point your browser at http://www.perl.org/, the Perl Home Page.

              perl hotti.pl
              Dateilaenge nach Schreiben: 4 Byte
              Dateilaenge nach sysopen: 4 Byte

              Auf Windows 2000, Strawberry Perl:

              perl -v

              This is perl, v5.10.0 built for MSWin32-x86-multi-thread

              Copyright 1987-2007, Larry Wall

              Perl may be copied only under the terms of either the Artistic License or the
              GNU General Public License, which may be found in the Perl 5 source kit.

              Complete documentation for Perl, including FAQ lists, should be found on
              this system using "man perl" or "perldoc perl".  If you have access to the
              Internet, point your browser at http://www.perl.org/, the Perl Home Page.

              perl hotti.pl
              Dateilaenge nach Schreiben: 4 Byte
              Dateilaenge nach sysopen: 3 Byte

              Auf Windows XP, Strawberry Perl:

              perl -v

              This is perl, v5.10.1 (*) built for MSWin32-x86-multi-thread

              Copyright 1987-2009, Larry Wall

              Perl may be copied only under the terms of either the Artistic License or the
              GNU General Public License, which may be found in the Perl 5 source kit.

              Complete documentation for Perl, including FAQ lists, should be found on
              this system using "man perl" or "perldoc perl".  If you have access to the
              Internet, point your browser at http://www.perl.org/, the Perl Home Page.

              perl hotti.pl
              Dateilaenge nach Schreiben: 4 Byte
              Dateilaenge nach sysopen: 3 Byte

              Tja, das stinkt nach einem Problem mit Windoof. Und genau bei 0x1A = Ctrl-Z = Text EOF. Wenn das mal ein Zufall ist.

              Perl öffnet Dateien normalerweise im Textmodus, genau wie C und dessen libc. Das ist bei Unixen völlig egal, bei Windoof spielt die libc massiv mit rein und macht lustige Sachen wie \n <=> CR+LF. binmode schaltet diesen Unfug für Binärdateien aus, das sehe ich bei Deinem Script aber nirgendwo. Und sysopen bringt alle Qualen der libc mit sich, während das normale open ohne den Ärger auskommt.

              binmode hinter jedem sysopen bringt nichts, denn wenn sysopen fertig ist, ist das Kind schon in den Brunnen gefallen.

              Das zweite sysopen öffnet übrigens zum SCHREIBEN und lesen, und erzeugt notfalls auch noch eine Datei (O_RDWR|O_CREAT). Ändere ich das auf O_RDONLY, bleibt die Datei 4 Bytes lang.

              Ganz offensichtlich meint die libc, dass die Datei eine Text-Datei ist, und bei der ist das finale Ctrl-Z eben optional und wird bei Gelegenheit gnadenlos entfernt.

              Laß also die Finger vom sysopen und alles wird gut.

              Oder sorge dafür, dass auch die libc kapiert, dass die Datei keine Textdatei ist (O_BINARY). Mit O_BINARY als zusätzliches Flag in deinem Test bleibt die Datei ebenfalls unbeschädigt.

              Wenn ich die sysopen durch reguläre 3-Argument-open ersetze, mit '+<' beim ersten und '<' beim zweiten, bleibt die Datei ebenfalls 4 Bytes lang.

              Ich kann das zweite open auch auf '+<' ändern, ohne dass die Datei beschädigt wird. Da die Datei binär ist, würde ich zur Sicherheit das Mode-Argument um ":raw" ergänzen bzw. für antike Perls nach dem open ein binmode einfügen.

              Warum mußt Du eigentlich die Zahl auf Krampf binär speichern? Ich kenne keine Hardware und kein Betriebssystem auf dem Perl 5 läuft und eine Blockgröße von weniger als 512 Bytes hat. Du schreibst und liest also immer mindestens 512 Bytes auf die / von der Platte (bzw. die Caches). Da ist es dann völlig egal, ob Du 4 Bytes nutzt oder den Integer dezimal auf 1 bis 10 Bytes verteilst.

              Alexander

              --
              Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
              1. Moin, moin,

                vielen Dank für Deine ausführliche und professionelle Antwort!!!!

                Oder sorge dafür, dass auch die libc kapiert, dass die Datei keine Textdatei ist (O_BINARY). Mit O_BINARY als zusätzliches Flag in deinem Test bleibt die Datei ebenfalls unbeschädigt.

                Das isses: O_BINARY und alles wird gut (geprüft). binmode(FH) ist eben doch nicht alles ;)

                Warum mußt Du eigentlich die Zahl auf Krampf binär speichern?

                Da muss ich weiter ausholen. Kurze Antwort: Hier stand die Idee Pate, nur eine Zahl zu speichern (zum Verwalten fortlaufender Nummern für diverse Anwendungen). Lange Antwort: In einer sequentiellen Verarbeitung von Dateien (danke Niklas Wirth) brauche ich Längenangaben die stets immer die gleiche Anzahl an Bytes haben, so erzeugt pack() mit Schablone "N" aus einem 32-Bit-Integer ebendiese 4 byte (32 Bit), egal ob die Zahl in dezimal ein, zwei, drei oder sonststellig ist. Freilich habe ich hier auch Dateien rumschwirren, die nicht nur 4 Byte haben, bisher stand da jedoch nie 0x1A am Ende sondern Nullterminierte Zeichenketten.

                Nochmal: Ein herzliches Dankeschön für Deine Ausführungen zu libc! Hab Einiges dazugelernt.

                Schönen Sonntag,
                Grüße an Alle,
                Horst Hurtig