Viewed   61 times
file_put_contents ( "file", "data", LOCK_EX )

for writing (which means - aquire lock and write)

file_get_contents ( "file", LOCK_EX )

for reading (which means - aquire lock and then read)

will it throw exception? raise an error? block until lock is aquired? or at least - should it? is there a chance that php will behave like this one day?

EDIT: i know that it is possible to use rename - i'd like to know answer to this...

 Answers

5

Since this answer is long, here's the summary: No, file_get_contents() is not atomic as it does not respect advisory locks.

About file locks in PHP:

In PHP, while on a *nix platform, filesystem locking is advisory only. Per the docs (Emphasis mine):

PHP supports a portable way of locking complete files in an advisory way (which means all accessing programs have to use the same way of locking or it will not work). By default, this function will block until the requested lock is acquired; this may be controlled (on non-Windows platforms) with the LOCK_NB option documented below.

So, as long as all of the processes that are accessing the file use this method of locking, you're fine.

However, if you're writing a static HTML file with a sane webserver, the lock will be ignored. In the middle of the write, if a request comes in, Apache will serve the partially written file. The locks will have no effect on the other process reading the lock.

The only real exception is if you use the special mount option of -o mand on the filesystem to enable mandatory locking (but that's not really used much, and can have a performance penalty).

Have a read on File Locking for some more information. Namely the section under Unix:

This means that cooperating processes may use locks to coordinate access to a file among themselves, but programs are also free to ignore locks and access the file in any way they choose to.

So, in conclusion, using LOCK_EX will create an advisory lock on the file. Any attempt to read the file will block only if the reader respects and/or checks for the lock. If they do not, the lock will be ignored (since it can be).

Try it out. In one process:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

And while it's sleeping, call this:

$f = fopen('test.txt', 'a+');
fwrite($f, 'foobar');
fclose($f);

The output will be foobar...

About file_get_contents specifically:

To your other specific question, first off, there is no LOCK_EX parameter to file_get_contents. So you can't pass that in.

Now, looking at the source code, we can see the function file_get_contents defined on line 521. There are no calls to the internal function php_stream_lock as there are when you pass file_put_contents('file', 'txt', LOCK_EX); defined on line 589 of the same file.

So, let's test it, shall we:

In file1.php:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

In file2.php:

var_dump(file_get_contents('test.txt'));

When run, file2.php returns immediately. So no, it doesn't appear that file_get_contents respects file locks at all...

Wednesday, November 9, 2022
5

A write that's under the size of 'PIPE_BUF' is supposed to be atomic. That should be at least 512 bytes, though it could easily be larger (linux seems to have it set to 4096).

This assume that you're talking all fully POSIX-compliant components. For instance, this isn't true on NFS.

But assuming you write to a log file you opened in 'O_APPEND' mode and keep your lines (including newline) under 'PIPE_BUF' bytes long, you should be able to have multiple writers to a log file without any corruption issues. Any interrupts will arrive before or after the write, not in the middle. If you want file integrity to survive a reboot you'll also need to call fsync(2) after every write, but that's terrible for performance.

Clarification: read the comments and Oz Solomon's answer. I'm not sure that O_APPEND is supposed to have that PIPE_BUF size atomicity. It's entirely possible that it's just how Linux implemented write(), or it may be due to the underlying filesystem's block sizes.

Wednesday, October 26, 2022
 
keeg
 
5

I'd recommend NPOI. NPOI is FREE and works exclusively with .XLS files. It has helped me a lot.

Detail: you don't need to have Microsoft Office installed on your machine to work with .XLS files if you use NPOI.

Check these blog posts:

Creating Excel spreadsheets .XLS and .XLSX in C#

NPOI with Excel Table and dynamic Chart

[UPDATE]

NPOI 2.0 added support for XLSX and DOCX.

You can read more about it here:

NPOI 2.0 series of posts scheduled

Sunday, August 28, 2022
 
1

The C standard does not define whether it is atomic or not.

In practice, you never write code which fails if a given operation is atomic, but you might well write code which fails if it isn't. So assume it isn't.

Saturday, October 29, 2022
18

I would use NTFS instead. It can easily be read/written to by Linux, Windows and OS X (among others).

Considering the intended use and the fact that it is an external drive, I see no reason why you should go through the hassle that using ext4 will be.

If this were your primary hard drive, perhaps the advantages of ext4 (briefly, less drive fragmentation and slightly improved performance, for more information see here) would be worthwhile. I doubt you will even notice a difference on an external USB drive. The bottleneck will be the rate of data transfer over the USB cable.

For most users, the only obvious benefit of ext4 over NTFS is that ext4 allows file names with special characters (? & % etc). Such file names, however, are both a bad idea and of no particular interest to you.


EDIT: In answer to B. Roland's question about defragmenting, yes, there are two ways I know of to defragment an NTFS drive under linux. One is an open source tool called shake. I haven't used it but it seems to work. The other is simply copying all data from the external drive, deleting everything from the drive and then copying everything back. Finally, since your drive will also be accessed under windows you can use the windows defragmenter.

I think, however, that you are attaching way too much importance to drive defragmentation. Have a look here for a simple explanation of fragmentation. Briefly, a fragmented drive is one where the files on the drive are not stored in contiguous blocks but spread out across the drive. This is usually caused by changing a file's size after it has been written. So, say you save a 1 MB file on your drive. You then modify the file and its size goes up to 20 MB. If there is not enough free space at the position where the file was created, this extra data will be written to a different part of the hard drive. In other words, the file will be fragmented.

This kind of thing is not likely to occur very often in a drive used for storing "backups, then family movies, pictures. Some music, and etc". Most of these files will never change in size, and therefore, will never fragment the drive.

In addition, even if your drive is fragmented, you don't really care. Fragmentation can be a problem for system drives where you can have many I/O operations per second. In those cases you can notice a decrease in drive performance. As I said before, on an external drive I don't think you will be able to detect this, even if it does happen.

So, in conclusion, you almost certainly will not need to defragment an NTFS formatted external HDD. However, if you really want to, you can do it both in Linux and in Windows.

Friday, December 9, 2022
 
ethan_g
 
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 :