Viewed   88 times

I am making a simple page load counter by storing the current count in a file. This is how I want to do this:

  1. Lock the file (flock)
  2. Read the current count (fread)
  3. Increment it (++)
  4. Write new count (fwrite)
  5. Unlock file/close it (flock/fclose)

Can this be done without losing the lock?

As I understand it, the file can't be written to without losing the lock. The only way I have come up with to tackle this, is to write a character using "r+" mode, and then counting characters.

 Answers

2

As said, you could use FLock. A simple example would be:

//Open the File Stream
$handle = fopen("file.txt","r+");

//Lock File, error if unable to lock
if(flock($handle, LOCK_EX)) {
    $count = fread($handle, filesize("file.txt"));    //Get Current Hit Count
    $count = $count + 1;    //Increment Hit Count by 1
    ftruncate($handle, 0);    //Truncate the file to 0
    rewind($handle);           //Set write pointer to beginning of file
    fwrite($handle, $count);    //Write the new Hit Count
    flock($handle, LOCK_UN);    //Unlock File
} else {
    echo "Could not Lock File!";
}

//Close Stream
fclose($handle);
Friday, October 21, 2022
3

What is the difference between lockf and fcntl:

On many systems, the lockf() library routine is just a wrapper around fcntl(). That is to say lockf offers a subset of the functionality that fcntl does.

Source

But on some systems, fcntl and lockf locks are completely independent.

Source

Since it is implementation dependent, make sure to always use the same convention. So either always use lockf from both your processes or always use fcntl. There is a good chance that they will be interchangeable, but it's safer to use the same one.

Which one you chose doesn't matter.


Some notes on mandatory vs advisory locks:

Locking in unix/linux is by default advisory, meaning other processes don't need to follow the locking rules that are set. So it doesn't matter which way you lock, as long as your co-operating processes also use the same convention.

Linux does support mandatory locking, but only if your file system is mounted with the option on and the file special attributes set. You can use mount -o mand to mount the file system and set the file attributes g-x,g+s to enable mandatory locks, then use fcntl or lockf. For more information on how mandatory locks work see here.

Note that locks are applied not to the individual file, but to the inode. This means that 2 filenames that point to the same file data will share the same lock status.

In Windows on the other hand, you can actively exclusively open a file, and that will block other processes from opening it completely. Even if they want to. I.e., the locks are mandatory. The same goes for Windows and file locks. Any process with an open file handle with appropriate access can lock a portion of the file and no other process will be able to access that portion.


How mandatory locks work in Linux:

Concerning mandatory locks, if a process locks a region of a file with a read lock, then other processes are permitted to read but not write to that region. If a process locks a region of a file with a write lock, then other processes are not permitted to read nor write to the file. What happens when a process is not permitted to access the part of the file depends on if you specified O_NONBLOCK or not. If blocking is set it will wait to perform the operation. If no blocking is set you will get an error code of EAGAIN.


NFS warning:

Be careful if you are using locking commands on an NFS mount. The behavior is undefined and the implementation widely varies whether to use a local lock only or to support remote locking.

Sunday, September 25, 2022
 
sitilge
 
3

The very idea of what you are trying to do is flawed. The file starts as

H  i  /  t  h  i  s  /  ...

If you were to change it in place, it would look as follows after processing the first line:

<  H  i  >  /  i  s  /  ...

Notice how you clobbered "th"? You need to make a copy of the file, modify the copy, the replace the original with the copy.

The simplest way is to make this copy in memory.

my $file;
{ # Read the file
   open(my $fh, '<', $qfn)
      or die "Can't open "$qfn": $!n";
   local $/;
   $file = <$fh>;
}

# Change the file
$file =~ s/^(.*)n/<$1>n/mg;

{ # Save the changes
   open(my $fh, '>', $qfn)
      or die "Can't create "$qfn": $!n";
   print($fh $file);
}

If you wanted to use the disk instead:

rename($qfn, "$qfn.old")
   or die "Can't rename "$qfn": $!n";

open(my $fh_in, '<', "$qfn.old")
      or die "Can't open "$qfn": $!n";
open(my $fh_out, '>', $qfn)
      or die "Can't create "$qfn": $!n";

while (<$fh_in>) {
   chomp;
   $_ = "<$_>";
   print($fh_out "$_n");
}

unlink("$qfn.old");

Using a trick, the above can be simplified to

local @ARGV = $qfn;
local $^I = '';
while (<>) {
   chomp;
   $_ = "<$_>";
   print(ARGV "$_n");
}

Or as a one-liner:

perl -i -pe'$_ = "<$_>"' file
Tuesday, August 2, 2022
 
adjan
 
4
QFile file("somefile.bin");

if (file.open(QIODevice::WriteOnly)) {
    QDataStream stream(&file);
    stream << *(model->invisibleRootItem());
    file.close();
}

http://qt-project.org/doc/qt-5.0/qtgui/qstandarditemmodel.html#invisibleRootItem


Edit:

Here is correction (I've checked that it works).

void MainWindow::save()
{
    QFile file("somefile.bin");
    if (file.open(QIODevice::WriteOnly)) {
        QDataStream stream(&file);
        qint32 n(model->rowCount()), m(model->columnCount());
        stream << n << m;

        for (int i=0; i<n; ++i)
            for (int j=0; j<m; j++)
                model->item(i,j)->write(stream);
        file.close();
    }
}

void MainWindow::load()
{
    QFile file("somefile.bin");
    if (file.open(QIODevice::ReadOnly)) {
        QDataStream stream(&file);
        qint32 n, m;
        stream >> n >> m;

        model->setRowCount(n);
        model->setColumnCount(m);
        for (int i=0; i<n; ++i)
            for (int j=0; j<m; j++)
                model->item(i,j)->read(stream);
        file.close();
    }
}
Tuesday, November 29, 2022
 
5

You also need to close the FileStream. At a minimum, you need to close it when your service exits, or when the FileStream would go out of the application's scope.

You should be able to open it as ReadOnly from another application either way, but you have to specify that, it's not a default.

In your service you need to enable the file sharing:

FileStream fs = new FileStream("path", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);

And in your reader application:

FileStream fs = new FileStream("path", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

Without the FileShare.Read, all requests to open the file for reading fail. Any other application requesting to open the file for writing will still fail, for write-enabled sharing you'd use FileShare.ReadWrite. The default option for FileShare is None.

Thursday, November 3, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :