returning page before long process is finished using C
7 answers - 1658 bytes -

I looked up the faq and found this, but found it very vague.
3.8: Can I launch a long process and return a page before it's
finished?
[UNIX]
You have to fork/spawn the long-running process.
The important thing to remember is to close all its file descriptors;
otherwise nothing will be returned to the browser until it's finished.
The standard trick to accomplish this is redirection to/from /dev/null:
"long_process < /dev/null /dev/null 2>&1 &"
print HTML page as usual
(don't take "exec" as literal in anything but a shell script - in C,
Perl, etc use fork+exec or system() :-)
i looked up fork and exec and found that there are many different exec*
calls so I am clueless on how to use this. Can anyone give me an
example or point me on how to fork a process using C? The cgi process
could take anywhere from 5 secs or a day depending on the input data so
I want to have the current webpage, where the form is, forward
immediately to a page having a message display, "your data is currently
being processed and will be available at http:// once complete."
After the data is processed, this page will be refreshed to the
resulting page that the cgi processed. Here is a newbie attempt at
this.
pid = fork();
if (pid == 1){
// call the exec here?
printf("Location: %s\n\n", szURL); // this is the page
i want to display right away
_exit(0);
}
else if (pid < 0) {
// print some error message that couldn't fork
_exit(0);
}
else {
// do the long processing here?
}
Any help is appreciated. Thanks.
No.1 | | 691 bytes |
| 
vduber6er wrote:
pid = fork();
if (pid == 1){
ITYM 0. That means you're in the child process, which is where
you want to do the processing (close your filedescriptors
and/or detach your session first).
// call the exec here?
printf("Location: %s\n\n", szURL); // this is the page
i want to display right away
_exit(0);
Why _exit rather than exit() ?
}
else if (pid < 0) {
// print some error message that couldn't fork
_exit(0);
}
else {
// do the long processing here?
No, this is the parent process, which should return an immediate
page to the user.
}
Any help is appreciated. Thanks.
No.2 | | 1218 bytes |
| 
Here is a perl example that explains how to use fork and exec together
with nearly failproof error checking:
use Errno qw(EAGAIN);
FRK:
if($pid = fork) {
#you are now the parent process
#this is where you would want to print out the html page
} elsif(defined $pid) {
#this is the child process
#this is where you would exec the longer process
exec('longer_process.pl') or die "Could not exec process";
} elsif($! == EAGAIN) {
#an error occured while trying to fork a new process
#this is a recoverable error so wait a little then try again
sleep 5;
redo FRK;
} else {
#an error occured while trying to fork that is not recoverable
die "could not fork a process";
}
}
A couple of notes:
1. I didn't test this code.
2. If the user were to close the browser window before the second
process is done, the second process will terminate as well. to get
around this you might want to use wait(); in the code for the parent
process. You might do something like this:
#print your HTML page
print "<br>Processing <br>";
wait($pid);
print "<br>Done Processing<br>";
hope this helps
No.3 | | 263 bytes |
| 
Ahh sorry forgot something in my last post in C the process is
very similar find a good C reference book that explains how to use
fork and exec properly the book is excelent (mine is at
home) I don't remember how anymore been doing perl to long now.
No.4 | | 345 bytes |
| 
Another question. Could I do this all within the long process? All the
code (that creates the cgi and has the long data processing) is in
mycode.c. Could I put the, printing html, fork and exec stuff in
mycode.c or does it have to be in some other file that would exec the
cgi file that mycode.c generates? Hope I make sense. Thanks.
No.5 | | 1009 bytes |
| 
definitly, just replace exec('long_process.pl'); with the code for the
long process.
just another note
This is how the fork command works:
if fork succeeds it returns the process id ($pid) of the child
process to the parent process and 0 to the child process. if it fails
it returns undef. File descriptors are shares between processes
but everything else is not.
It's good proctice to remember to end a child node with exit(); This
way your child node doesn't inadvertantly execute code that is only
ment for the parent.
Always remember to wait(); in the parent node before closing out. If
you don't do this, your child process will become a zombie process.
Also, be carefull using fork() on a windows system, there serious
performance issues with this. Windows has it's own set of functions
for spawning processes and the such. I know in perl they are all
accessable from the Win32::Process module not sure in C.
No.6 | | 954 bytes |
| 
I tried to do this but it seems like I don't get a web page returning
right away. It is still waiting for the process to finish. I never
get the process waiting page. I think I accounted for all file
descriptors, but there could still be some out there. than file
descriptors could there be anything else keeping me from displaying the
webpage right away. I have tried closing stdout and stdin and leaving
them both open but it still doesnt work. Here is what i have. szURL
is where the process waiting page resides.
pid = fork();
if (pid){
fclose (logFile); // file descriptors that i have next
3 lines
fclose (inStream);
fclose (outStream);
fflush (stdout);
fclose (stdout);
fclose (stdin);
printf("Location: %s\n\n", szURL);
wait();
exit(0);
}
if (pid < 0){
// prints error message
return 0;
}
if (pid == 0 ) {
// does long process
}
Thanks a lot!
No.7 | | 1307 bytes |
| 
Again, I'm not to sure how this is done in C but in perl you can
execute a process by placing it in ` marks.
like you said in your original post:
"long_process < /dev/null /dev/null 2>&1 &"
Combining it with the forking example I gave you gives:
use Errno qw(EAGAIN);
FRK:
if($pid = fork) {
#you are now the parent process
#this is where you would want to print out the html page
} elsif(defined $pid) {
#this is the child process
#this is where you would exec the longer process
`longer_process.pl < /dev/null /dev/null 2>&1 &`;
} elsif($! == EAGAIN) {
#an error occured while trying to fork a new process
#this is a recoverable error so wait a little then try again
sleep 5;
redo FRK;
} else {
#an error occured while trying to fork that is not recoverable
die "could not fork a process";
}
}
I tried this on my server where the main program just prints out the
word done and long_process.pl counted from 1-100000 a million times
the html page loaded in my browser in milliseconds and I could still
see the long_process.pl running in the background on the server process
list. When I closed the browser window it killed the
long_process.pl process though (just a note).