Please explain why the code below blocks first execution (1607061065) and does not block the second one (1664361639) (the output is attached):
<?php
$fileName = "file.txt";
$fp = fopen($fileName, "a");
if (flock($fp, LOCK_EX | LOCK_NB)) {
$runId = rand();
fwrite(
$fp,
"processing task [runId]: $runId; [pid]: ". getmypid() ." started \n" .
(new DateTime())->format(DateTime::ATOM) . " \n\n"
);
sleep(3);
flock($fp, LOCK_UN);
fwrite(
$fp,
"processing task [runId]: $runId; [pid]: ". getmypid() ." finished \n" .
(new DateTime())->format(DateTime::ATOM) . " \n\n"
);
fclose($fp);
exit;
}
fwrite(
$fp,
"script is busy [pid]: ". getmypid() ." \n" .
(new DateTime())->format(DateTime::ATOM) . " \n\n"
);
I am running this via ab utility which is able to send request concurrently:
ab -c 3 -n 8 http://localhost/
(it is run by 3 instances in parallel 8 times)
And this is what I have in file.txt after execution is finished:
processing task [runId]: 1607061065; [pid]: 2507 started
2022-12-22T17:03:22+00:00
processing task [runId]: 1607061065; [pid]: 2507 finished
2022-12-22T17:03:25+00:00
processing task [runId]: 1664361639; [pid]: 2503 started
2022-12-22T17:03:25+00:00
script is busy [pid]: 2507
2022-12-22T17:03:25+00:00
script is busy [pid]: 2502
2022-12-22T17:03:25+00:00
script is busy [pid]: 2502
2022-12-22T17:03:25+00:00
script is busy [pid]: 2507
2022-12-22T17:03:25+00:00
script is busy [pid]: 2502
2022-12-22T17:03:25+00:00
script is busy [pid]: 2507
2022-12-22T17:03:25+00:00
processing task [runId]: 1664361639; [pid]: 2503 finished
2022-12-22T17:03:28+00:00
Why 1607061065 blocks the first execution and why there is a second process exist which acuires lock 1664361639 which does not block the execution.
Thanks in advance
Related
My code:
function log_message($user = "", $message = "") {
$log_file = LOG_PATH;
// if we can open the file
if($handle = fopen($log_file, "a")) {
$timestamp = strftime("%Y-%m-%d:%H %H:%M:%S", time());
$content = $timestamp . " | [{$user}] | " . $message . "\n";
fwrite($handle, $content);
fclose($handle);
} else {
die("error opening log file: {$log_file}");
}
}
Problem:
The first time I can write to the file no problem but any subsequent writes don't get make it through and falls into the else statement?
Other info:
The site is hosted on godaddy and i have set the privacy permissions on the folder to be writable.
What I tried:
I used the function
file_put_contents($log_file, $content . PHP_EOL, FILE_APPEND | LOCK_EX);
but same result.
I also tried the solution mentioned in this question: How to make sure a file handle has been closed before next operation?
I've implemented a file optimization script which takes about 30 seconds or more. There's a loop in which I added echos to track what's being processed.
However, most of the time, no output is being sent, until the end of the process.
How can I control this in order to send the echos in an iteration just as they're being finished?
EDIT:
This is the implied code, with the output buffer functions:
set_time_limit(60);
ini_set("memory_limit", "256M");
$documents = $this->documents_model->get($date1, $date2);
ob_start();
echo '-- Start ' . "<br>\n";
ob_end_flush();
flush();
foreach ($documents as $document) {
ob_start();
echo '-- Processing document ' . $document->id . "<br>\n";
$file = $document->get_file_name();
if (! $file || ! file_exists(DOCUMENT_ROOT . 'documents/' . $file)) {
echo '---- Document ' . $document->id . " has no PDF file yet or it was deleted<br>\n";
$path = $this->documents_model->generatePDF($document);
echo '------ file generated: ' . $path;
}
ob_end_flush();
flush();
}
echo '-- End ' . "<br>\n";
The reason why you will get the output after the processing finishes, is php's output buffer and eventually your webserver's buffer.
You will have to flush these buffers with the invokation of ob_flush() and flush() after every few echo statements.
For more technical information see PHP buffer ob_flush() vs. flush()
I've got a form that writes its input to a textfile.
Would it be possible to lock a text file for editing, and perhaps give a friendly message "the file is edited by another user, please try again later."
I'd like to avoid conflicts if the file has multiple editors at the same time.
Here's how the entry is currently added.
$content = file_get_contents("./file.csv");
$fh = fopen("./file.csv", "w");
fwrite($fh, $date_yy . '-' . $date_mm . '-' . $date_dd . '|' . $address . '|' . $person . '|' . $time_hh . ':' . $time_mm);
fwrite($fh, "\n" . $content);
fclose($fh);
Any thoughts?
You can use flock() function to lock the file. For more see this
Something like:
<?php
$content = file_get_contents("./file.csv");
$fp = fopen("./file.csv", "w"); // open it for WRITING ("w")
if (flock($fp, LOCK_EX))
{
// do your file writes here
fwrite($fh, $date_yy . '-' . $date_mm . '-' . $date_dd . '|' . $address . '|' . $person . '|' . $time_hh . ':' . $time_mm);
fwrite($fh, "\n" . $content);
fclose($fh);
flock($fh, LOCK_UN); // unlock the file
}
?>
In order of desirability:
Use a database.
Use more than one text file.
Use locks:
eg:
$lockwait = 2; // seconds to wait for lock
$waittime = 250000; // microseconds to wait between lock attempts
// 2s / 250000us = 8 attempts.
$myfile = '/path/to/file.txt';
if( $fh = fopen($myfile, 'a') ) {
$waitsum = 0;
// attempt to get exclusive, non-blocking lock
$locked = flock($fh, LOCK_EX | LOCK_NB);
while( !$locked && ($waitsum <= $lockwait) ) {
$waitsum += $waittime/1000000; // microseconds to seconds
usleep($waittime);
$locked = flock($fh, LOCK_EX | LOCK_NB);
}
if( !$locked ) {
echo "Could not lock $myfile for write within $lockwait seconds.";
} else {
// write out your data here
flock($fh, LOCK_UN); // ALWAYS unlock
}
fclose($fh); // ALWAYS close your file handle
} else {
echo "Could not open $myfile";
exit 1;
}
You can use PHP's flock function to lock a file for writing, but that lock won't persist across web requests and doesn't work on NFS mounts (at least in my experience).
Your best be may be to create a token file in the same directory, check for its existence and report an error if it exists.
As with any locking scheme, you're going to have race conditions and locks that remain after the operation has completed, so you'll need a way to mitigate those.
I would recommend creating a hash of the file before editing and storing that value in the lock file. Also send that hash to the client as part of the edit form (so it comes back as data on the commit request). Before writing, compare the passed hash value to the value in the file. If they are the same, commit the data and remove the lock.
If they are different, show an error.
You could try flock — Portable advisory file locking ?
http://php.net/manual/en/function.flock.php
I would just use a simple integer or something like that.
$content = file_get_contents("./file.csv");
$fh = fopen("./file.csv", "w");
$status = 1;
...
if($status == 1){
fwrite($fh, $date_yy . '-' . $date_mm . '-' . $date_dd . '|' . $address . '|' . $person . '|' . $time_hh . ':' . $time_mm);
fwrite($fh, "\n" . $content);
fclose($fh);
$status = 0;
}
else
echo "the file is edited by another user, please try again later.";
Is that what you mean?
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
php exec command (or similar) to not wait for result
exec() waiting for a response in PHP
I have a php script that calls and runs a Matlab script. The result of the Matlab script is a .png image, which I would then like to load in php and send to a webpage. The php code I have is:
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
passthru($command);
$im = file_get_contents('C:\\habitat.png');
header('Content-type:image/png');
echo $im;
However, it appears that after sending the 'passthru' command, php does not wait for the Matlab script to finish running. Thus, if the image file does not exist before running the php code, then I get an error message.
Is there a way to make it so that the php code waits for the Matlab script to finish running before it attempts to load the image file?
passthru is not the main issue here .. but i guess as soon you have a response from your command the image is not written instantly but by a 3rd process
file_get_contents might also fail in this instance because .. The image might not be written once or in the process of writing which can result to file lock .. in any case you need to be sure you have a valid image before output is sent;
set_time_limit(0);
$timeout = 30; // sec
$output = 'C:\\habitat.png';
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
try {
if (! #unlink($output) && is_file($output))
throw new Exception("Unable to remove old file");
passthru($command);
$start = time();
while ( true ) {
// Check if file is readable
if (is_file($output) && is_readable($output)) {
$img = #imagecreatefrompng($output);
// Check if Math Lab is has finished writing to image
if ($img !== false) {
header('Content-type:image/png');
imagepng($img);
break;
}
}
// Check Timeout
if ((time() - $start) > $timeout) {
throw new Exception("Timeout Reached");
break;
}
}
} catch ( Exception $e ) {
echo $e->getMessage();
}
I believe if you change passthru to exec it will work as intended. You can also try this:
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
passthru($command);
// once a second, check for the file, up to 10 seconds
for ($i = 0; $i < 10; $i++) {
sleep(1);
if (false !== ($im = #file_get_contents('C:\\habitat.png'))) {
header('Content-type:image/png');
echo $im;
break;
}
}
I would like to daemonize a php script (Jobque.php) under PHP 5.3 in Linux
The idea is to call the script like this:
php -f ./application/Model/Jobque.php start
and than the script would do a shell_exec to put itself in the background and do the processing like this
nohup php -f /var/.../application/Model/Jobque.php process 2> /dev/null & echo $!
this last command indeed starts the script in the background and all is fine, however when I issue this command from within the script itself the script waits until the execution stops (never)
this is the function I use to start the script as a daemon - the Windows part works
public function start_daemon() {
if (file_exists ( $this->pidfile ))
die ( 'process is already running - process pidfile already exists -> ' . $this->pidfile );
$cmd = 'php -f ' . __FILE__ . ' process';
if (substr ( php_uname (), 0, 7 ) == "Windows") {
$WshShell = new COM ( "WScript.Shell" );
$oExec = $WshShell->Run ( "$cmd /C dir /S %windir%", 0, false );
exec ( 'TASKLIST /NH /FO "CSV" /FI "imagename eq php.exe" /FI "cputime eq 00:00:00"', $output );
$output = explode ( '","', $output [0] );
$pid = $output [1];
file_put_contents ( $this->pidfile, $pid );
} else {
$execstr = "nohup $cmd 2> /dev/null & echo $!";
//echo $execstr; -- the execstr is right in itself
$PID = shell_exec ( $execstr );
//echo $PID; -- we never get here
file_put_contents ( $this->pidfile, $PID );
}
echo ('JobQue daemon started with pidfile:' . $this->pidfile);
}
what am I doing wrong here, and how to do it right?
Take a look at: http://pear.php.net/package/System_Daemon
I ended up using pcntl_fork for daemonizing the script under Linux, like this:
public function start_daemon($worker) {
if (file_exists ( $this->get_pidfile ( $worker ) ))
die ( 'process is already running - process pidfile already exists -> ' . $this->get_pidfile ( $worker ) . "\n" );
$cmd = 'php -f ' . __FILE__ . ' process';
if ($this->is_win) {
$WshShell = new COM ( "WScript.Shell" );
$oExec = $WshShell->Run ( "$cmd /C dir /S %windir%", 0, false );
exec ( 'TASKLIST /NH /FO "CSV" /FI "imagename eq php.exe" /FI "cputime eq 00:00:00"', $output );
$output = explode ( '","', $output [0] );
$pid = $output [1];
file_put_contents ( $this->get_pidfile ( $worker ), $pid );
echo ('JobQue daemon started with pidfile:' . $this->get_pidfile ( $worker ) . "\n");
} else {
$PID = pcntl_fork ();
if ($PID) {
file_put_contents ( $this->get_pidfile ( $worker ), $PID );
echo ('JobQue daemon started with pidfile:' . $this->get_pidfile ( $worker ) . "\n");
exit (); // kill parent
}
posix_setsid (); // become session leader
chdir ( "/" );
umask ( 0 ); // clear umask
$this->process_jobs (); //start infinite loop
}
}
Take a look at the daemonize command: http://software.clapper.org/daemonize/index.html
Or, "upstart" on ubuntu: http://upstart.ubuntu.com/