I have a PHP script that runs during about 1s (15s for my tests), and I would like to store the number of threads that are running this script to do a specific action when too many scripts/threads are running.
To do that I tried to store the number of opened tasks in a file.
I read the value in the file, increment, and write the new value at the beginning
Do my stuff
Read the value in the file, decrement, and write the new value at the beginning
Here is my code:
<?php
define("MAX_THREADS", 5);
define("LOCK_FILE", "threads.txt");
$fp = fopen(LOCK_FILE, "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
$threadNb = fread($fp, filesize(LOCK_FILE));
/*
* if $threadNb > MAX_THREAD => Do something
*/
ftruncate($fp, 0); // truncate file
fwrite($fp, ++$threadNb);
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
} else {
echo "Failed lock the file!";
}
fclose($fp);
// Do stuff
// ...
// End of script
sleep(15);
$fp = fopen(LOCK_FILE, "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
$threadNb = fread($fp, filesize(LOCK_FILE));
ftruncate($fp, 0); // truncate file
fwrite($fp, --$threadNb);
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
} else {
echo "Failed lock the file!";
}
fclose($fp);
PROBLEM: The value of $threadNb is always equal to 0!!
Related
I am trying to write to a file and then read the data from the same file. But sometimes I am facing this issue that the file reading process is getting started even before the file writing gets finished. How can I solve this issue ? How can i make file writing process finish before moving ahead?
// writing to file
$string= <12 kb of specific data which i need>;
$filename.="/ttc/";
$filename.="datasave.html";
if($fp = fopen($filename, 'w'))
{
fwrite($fp, $string);
fclose($fp);
}
// writing to the file
$handle = fopen($filename, "r") ;
$datatnc = fread($handle, filesize($filename));
$datatnc = addslashes($datatnc);
fclose($handle);
The reason it does not work is because when you are done writing a string to the file the file pointer points to the end of the file so later when you try to read the same file with the same file pointer there is nothing more to read. All you have to do is rewind the pointer to the beginning of the file. Here is an example:
<?php
$fileName = 'test_file';
$savePath = "tmp/tests/" . $fileName;
//create file pointer handle
$fp = fopen($savePath, 'r+');
fwrite($fp, "Writing and Reading with same fopen handle!");
//Now rewind file pointer to start reading
rewind($fp);
//this will output "Writing and Reading with same fopen handle!"
echo fread($fp, filesize($savePath));
fclose($fp);
?>
Here is more info on the rewind() method http://php.net/manual/en/function.rewind.php
I have mentioned the URL through which i got the solution. I implemented the same. If you want me to copy the text from that link then here it is :
$file = fopen("test.txt","w+");
// exclusive lock
if (flock($file,LOCK_EX))
{
fwrite($file,"Write something");
// release lock
flock($file,LOCK_UN);
}
else
{
echo "Error locking file!";
}
fclose($file);
Use fclose after writing to close the file pointer and then fopen again to open it.
I want some PHP to do the following in this order:
Gain exclusive lock to a file (waiting if already locked)
Read the contents of the file
Empty the file of all contents
Remove the lock
But any code I'm coming up with one way or another always relinquishes the lock between the reading and writing.
$fp = fopen('status.txt', 'r+');
flock($fp, LOCK_EX);
$str = fread($fp,1000); // [another hack. I just want it to read everything]
unlink('status.txt');
touch('status.txt');
Any ideas? I don't trust anything I do with files.
I think ftruncate can do what you want, since it works on a file that you already have open.
http://www.php.net/manual/en/function.ftruncate.php
Here's their example:
<?php
$filename = 'lorem_ipsum.txt';
$handle = fopen($filename, 'r+');
ftruncate($handle, rand(1, filesize($filename)));
rewind($handle);
echo fread($handle, filesize($filename));
fclose($handle);
?>
So I think what you want then is something like:
$fp = fopen('status.txt', 'r+');
flock($fp, LOCK_EX);
$str = fread($fp, filesize('status.txt'));
ftruncate($fp, 0);
flock($fp, LOCK_UN);
fclose($fp);
I'm trying to write to a file with PHP and this is the code I'm using (taken from this answer to my previous question):
$fp = fopen("counter.txt", "r+");
while(!flock($fp, LOCK_EX)) { // acquire an exclusive lock
// waiting to lock the file
}
$counter = intval(fread($fp, filesize("counter.txt")));
$counter++;
ftruncate($fp, 0); // truncate file
fwrite($fp, $counter); // set your data
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
fclose($fp);
The read part works fine, if the file gets read, it's content is read well, i.e. if the file contains 2289, then 2289 is read.
The problem is that when it increments and rewrites the value to that file, [NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]1 gets written.
What am I missing? Why do null characters get written?
The thing you are missing is rewind(). Without it, after you truncate to 0 bytes, the pointer is still not at the beginning (reference). So when you write your new value, it pads it with NULL in your file.
This script will read a file (or create if it doesn't exist) for a current count, increments, and writes it back to the same file every time the page loads.
$filename = date('Y-m-d').".txt";
$fp = fopen($filename, "c+");
if (flock($fp, LOCK_EX)) {
$number = intval(fread($fp, filesize($filename)));
$number++;
ftruncate($fp, 0); // Clear the file
rewind($fp); // Move pointer to the beginning
fwrite($fp, $number); // Write incremented number
fflush($fp); // Write any buffered output
flock($fp, LOCK_UN); // Unlock the file
}
fclose($fp);
EDIT #2:
Try this with flock (tested)
If file is not locked, it will throw an Exception (see added line) if(...
I borrowed the Exception snippet from this accepted answer.
<?php
$filename = "numbers.txt";
$filename = fopen($filename, 'a') or die("can't open file");
if (!flock($filename, LOCK_EX)) {
throw new Exception(sprintf('Unable to obtain lock on file: %s', $filename));
}
file_put_contents('numbers.txt', ((int)file_get_contents('numbers.txt'))+1);
// To show the contents of the file, you
// include("numbers.txt");
fflush($filename); // flush output before releasing the lock
flock($filename, LOCK_UN); // release the lock
fclose($filename);
echo file_get_contents('numbers.txt');
?>
You can use this code, a simplified version, but am not sure if it's the best:
<?php
$fr = fopen("count.txt", "r");
$text = fread($fr, filesize("count.txt"));
$fw = fopen("count.txt", "w");
$text++;
fwrite($fw, $text);
?>
My question is: can not using shared locks during reading cause write errors, even if the write operation is using exclusive locks?
Let's say I want to create a file-based counter, like this:
//increment counter by 1
$fp = fopen($path, 'r+b');
if (flock($fp, LOCK_EX)) {
//read
fseek($fp, 0, SEEK_END);
$size = ftell($fp);
fseek($fp, 0, SEEK_SET);
if ($size == 0) {
$counter = 0;
} else {
$data = fread($fp, $size);
$counter = intval($data);
}
//do something with data we just read
$counter ++;
//write
fseek($fp, 0, SEEK_SET);
ftruncate($fp, 0);
fwrite($fp, $counter);
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
} else {
fclose($fp);
throw new Exception("Lock failed");
}
Now I want to present it somewhere else:
echo intval(file_get_contents($path));
Note that file_get_contents does not use shared locks.
This code has proven to corrupt the data under heavy page load, i.e. counter was reset few times back to 0.
I changed code to use fopen and LOCK_SH and it seems okay for now, but I have no means to confirm that this indeed was source of the problem as I have no control over load. Local execution of above code using multiple CLI PHP instances suggested that code worked even with file_get_contents...
The comments in the manual for ftell discuss unpredictable behavior for files opened with append mode. Maybe you could try to open the file in read only for the read, or just read the file anyway, I don't see why you have a line in the code that can explicity set the counter to 0.
echo intval(false); //0
And fread at EOF returns false, so:
$fp = fopen($path, 'r+b');
if (flock($fp, LOCK_EX)) {
//read
$data = fread($fp, $size);
$counter = intval($data);
//do something with data we just read
$counter ++;
//write
fseek($fp, 0, SEEK_SET);
ftruncate($fp, 0);
fwrite($fp, $counter);
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
} else {
fclose($fp);
throw new Exception("Lock failed");
}
...finally, found the culprit. Our production server has flock function turned off and unfortunately we are in no position to change that. I guess I'll have to implement custom locking mechanism.
This is due to production server having NFS file system and locking files on NFS not working at all in most cases.
What would be the best way to change a specific line of a utf-8 encoded text file that has a specific definition like:
.
.
"VALIDATE_RESULT":"1",
"VERIFY_RTL":"0",
.
.
"SERVER_CRASH_MSG":"system is down",
I would like to flock, change, write and release.
I will use the code :
<?php
$fp = fopen("file.txt", "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
//make your changes
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
} else {
echo "Couldn't get the lock!";
}
fclose($fp);
?>
file_get_contents just does not cut it because it doesn't use a handle which flock requires.
Thanks