pl: LOCK_NB Verwendung

Moin,

angeregt durch einen Thread weiter unten, hab ich mich mal wieder mit flock() befasst, speziell mit dem Parameter NB was für Non Blocking steht. Um das Verhalten zu testen, verwende ich fork(), womit zwei Prozesse entstehen die zeitlich aufeinandertreffen. Jeder Prozess liest eine Zahl aus einer Datei, erhöht diese Zahl um eins und schreibt die Zahl zurück in die Datei. Schauen wir uns das Ergebnis an:

  1. flock($FH, LOCK_EX); Parent- und Childprozess bekommen jeweils eine fortlaufende und eindeutige Nummer (z.B. Parent: 1492, Child: 1493), dieses Verhalten ist für den Regelbetrieb auch so beabsichtigt.

  2. Die Datei wird nicht gelockt: Beide Prozesse erhalten einunddieselbe Nummer, typisch für eine Racecondition und so nicht erwünscht.

  3. flock($FH, LOCK_EX|LOCK_NB); Einer der beiden Prozesse, meistens ist das der Childprozess, manchmal aber auch der Parent, erhält keine fortlaufende Nummer (undef).

Nun frage ich mich, für was LOCK_NB gut sein soll. Hat mal jemand ein praktisches Beispiel für eine sinnvolle Verwendung für Non Blocking Lock?

Schöne Grüße.

  1. Hello,

    Nun frage ich mich, für was LOCK_NB gut sein soll. Hat mal jemand ein praktisches Beispiel für eine sinnvolle Verwendung für Non Blocking Lock?

    Man sollte die Rückgabewerte der benutzten Funktionen auch abfragen!
    Selbstverständlich musst Du bei Verwensung von LOCK_NB vorher fragen, ob gelockt werden konnte und nur dann schreibend auf die Datei zugreifen.

    Schau Dir auch den Parameter Wouldblock an. Wenn

    $lockock = flock($fp, LOCK_EX|LOCK_NB, $reason);
    
    if (!$lockok)
    {
        if ($reason === 1) 
        {
            echo "ich konnte nicht sperren, weil ich sonst hätte warten müssen auf das Lock";
        }
        else
        {
            echo "beim Lockversuch ist ein Fehler aufgetreten, 
                  näheres erfährst Du mit error_get_last()";
        }
    }
    

    Lock NB ist dafür da, dass der Prozess nicht wartet auf ein Lock, sondern der Programmierer den Rückgabewert von flock() abfragt, ob er sperren konnte. Das Programm kann also z. B. seine Schleife weiter ausführen und beim nächsten Umlauf nochmal versuchen zu sperren.

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Hello,

      Nun frage ich mich, für was LOCK_NB gut sein soll. Hat mal jemand ein praktisches Beispiel für eine sinnvolle Verwendung für Non Blocking Lock?

      Man sollte die Rückgabewerte der benutzten Funktionen auch abfragen!

      Selbstverständlich habe ich das auch gemacht. So wirft flock($FH, LOCK_EX|LOCK_NB) keinen Fehler und gibt TRUE zurück sowohl beim Parent als auch beim Client.

      Lock NB ist dafür da, dass der Prozess nicht wartet auf ein Lock, sondern der Programmierer den Rückgabewert von flock() abfragt, ob er sperren konnte.

      Nein, diese Logik passt ja nicht, siehe oben.

      Schöne Grüße.

      1. Hello,

        Nun frage ich mich, für was LOCK_NB gut sein soll. Hat mal jemand ein praktisches Beispiel für eine sinnvolle Verwendung für Non Blocking Lock?

        Man sollte die Rückgabewerte der benutzten Funktionen auch abfragen!

        Selbstverständlich habe ich das auch gemacht. So wirft flock($FH, LOCK_EX|LOCK_NB) keinen Fehler und gibt TRUE zurück sowohl beim Parent als auch beim Client.

        Lock NB ist dafür da, dass der Prozess nicht wartet auf ein Lock, sondern der Programmierer den Rückgabewert von flock() abfragt, ob er sperren konnte.

        Nein, diese Logik passt ja nicht, siehe oben.

        Wenn Du von "Parent" und "Client" sprichst, meist Du da vielleicht "Parent" und "Child"?

        Dass das Handle dann ggf. vererbt wurde und quasi dasselbe ist, ist dir schon klar?
        Mach zwei verschiedene Instanzen im Browser auf, die beide auf die Datei zugreifen, sie locken und anschließend in einer Schleife warten, bis Du eine Eingabe gemacht hast oder eine Kontrolldatei auf der Platte weggeputzt hast...

        Da siehst Du schon, dass ich Recht habe.

        Liebe Grüße
        Tom S.

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
        1. hi Tom,

          Wenn Du von "Parent" und "Client" sprichst, meist Du da vielleicht "Parent" und "Child"?

          Child.

          Dass das Handle dann ggf. vererbt wurde und quasi dasselbe ist, ist dir schon klar?

          Nein, es ist nicht dasselbe. Es wird in einer ganz anderen Package estellt. Perl:

          package LfdNr;
          
          # fortlaufende Nummern
          # SYNOPSIS
          # my $nr = do 'LfdNr.pm';
          
          
          use strict;
          use warnings;
          use IO::File;
          use Fcntl qw(:flock);
          
          sub lfdnr{
          
              my $file = "/home/netsh100633/files/nr.bin";
              my $fh = IO::File->new;
              $fh->open($file, O_CREAT|O_RDWR|O_BINARY) || die $!;
              flock( $fh, LOCK_EX|LOCK_NB ) || die "Cannot lock $!";
              read( $fh, my $bin, 4 );
              my $nr = unpack("N", $bin) || 1580;
              $nr += 1;
              
              $fh->truncate(0);
              $fh->seek(0,0);
              $fh->print( pack "N", $nr );
              $fh->close;
              
              return $nr;
          }
          
          my $nr = LfdNr::lfdnr();
          
          

          Und das CGI-Script zum Testen

          #!/usr/bin/perl
          
          use strict;
          use warnings;
          use v5.10;
          
          $SIG{__WARN__} = sub{ die @_ };
          
          $| = 1;
          
          print "Content-Type: text/plain\n\n";
          
          say eval{
              my $pid = fork;
              die "Can't fork" unless defined $pid;
          
              my $response = '';
              if( $pid == 0 ){
                  $response = "Child $$ with Nr.: ".do "LfdNr.pm";
              }
              else{
                  $response = "Parent $$ with Nr.: ".do "LfdNr.pm";
              }
          
              $response;
          } || $@;
          
          

          Was auch Warnungen in den Status einer Exception erhebt, wie in meinem Fall ein undefinierer Wert für $nr. So stirbt der andere Prozess weil $nr nicht definiert ist, sofern ein LOCK_NB gesetzt wurde.

          Das Verhalten ist unabhängig davon ob das OS ein advisory Lock oder mandatory Lock unterstützt.

          Schöne Grüße.

          1. Oh sorry, mein Fehler und systematischer Natur: eval unterdrückt die Exception die beim do LfdNr.pm auftritt sofern $nr undef ist. Die Exception tritt tatsächlich weiter oben auf, nämlich bei

            flock($FH, LOCK_EX|LOCK_NB) || 'Cannot Lock $!';

            Also ist das Verhalten unabhängi davon, in welcher Package das Handle erstellt und gelockt werden soll.

            Freundschaft 😉

        2. Jetzt wird interessant, Wenn ich alles zusammen in einer Package laufen lasse:

          #!/usr/bin/perl
          
          
          use strict;
          use warnings;
          use IO::File;
          use Fcntl qw(:flock);
          use v5.10;
          
          $SIG{__WARN__} = sub{ die @_ };
          
          $| = 1;
          print "Content-Type: text/plain\n\n";
          
          say eval{
              my $pid = fork;
              die "Can't fork" unless defined $pid;
          
              my $response = '';
              if( $pid == 0 ){
                  $response = "Child $$ with Nr.: ".lfdnr();
              }
              else{
                  $response = "Parent $$ with Nr.: ".lfdnr();
              }
          
              $response;
          } || $@;
          
          
          sub lfdnr{
          
              my $file = "/home/netsh100633/files/nr.bin";
              my $fh = IO::File->new;
              $fh->open($file, O_CREAT|O_RDWR|O_BINARY) || die $!;
              flock( $fh, LOCK_EX|LOCK_NB ) || die "Cannot lock $^E";
              read( $fh, my $bin, 4 );
              my $nr = unpack("N", $bin) || 1580;
              $nr += 1;
              
              $fh->truncate(0);
              $fh->seek(0,0);
              $fh->print( pack "N", $nr );
              $fh->close;
              
              return $nr;
          }
          

          Erscheint folgende Meldung:

          Parent 3872 with Nr.: 1803
          Cannot lock Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat
          

          und das auch wieder unabhängig davon ob advisory oder mandatory Lock vom OS unterstützt wird.

          Also:

          • Wenn in einer dedizierten Package mit LOCK_EX|LOCK_NB gelockt wird, gibt flock() TRUE zurück.

          • Wenn jedoch alles in einer Package abläuft, siehe oben, gibt flock() FALSE zurück.

          Ich frage mich trotz dieser unterschiedlichen Verhaltensweisen, für was ein LOCK_NB gut sein soll. Zum Erzeugen eindeutiger und fortlaufender Nummern macht ein LOCK_NB mit Sicherheit überhaupt keinen Sinn, denn die Anwendungen müssen sich darauf verlassen können daß sie eine eindeutige und fortlaufende Nummer bekommen.

          Schöne Grüße.

    2. Hi,

      Lock NB ist dafür da, dass der Prozess nicht wartet auf ein Lock, sondern der Programmierer den Rückgabewert von flock() abfragt, ob er sperren konnte.

      Dann würde ich das mal so in meiner Programmlogik entsprechend würdigen:

      flock( $FH, LOCK_EX | LOCK_NB ) 
         or die "Kann nicht locken, weil Datei bereits gelockt ist: $!"; 
      
      flock( $FH, LOCK_EX ) 
         or die "Kann nicht locken, weil es vom System nicht unterstützt wird: $!"; 
      

      lock ex + nb lock ex

      Schöne Grüße.