2

I am running my code on SWI Prolog and Windows 7. In my code, I call another application using 'win_exec()' (I tried using the method 'shell()', but it won't work.), and at the end produces its output in a text file. However sometimes it runs for times like 30 minutes. Now my prolog code uses this output text file, to further parse it and process it. This is the code I use to call the other application and then use its output file:

main(Value,X) :- win_exec('C:\\myfolder\\external_app.bat C:\\myfolder\\outputfile.txt', normal),
                 open('C:\\myfolder\\outputfile.txt', read, Mf),
                 read_file(Mf, X),  % PROCEDURE TO READ FILE CONTENTS 
                 close(Mf),
                 statistics(cputime, Value). % CALCULATE HOW LONG IT TOOK

However, since the file has not been outputted by that another application, it gives the error:

ERROR: open/4: source_sink `C:\myfolder\outputfile.txt' does not exist (No such file or directory)

So, as a workaround, I try to catch the error, handle it by comparing with 'existence_error' and then recursively call the open procedure, until it finally succeeds i.e. the other application has completed its processing and the output file is produced. This is my workaround code for this:

main(Value,X) :- win_exec('C:\\myfolder\\external_app.bat C:\\myfolder\\outputfile.txt', normal),
                 open_output(X),   % PROCEDURE FOR FILE EXCEPTION HANDLING
                 statistics(cputime,Value).

open_output(X) :- catch((open('C:\\myfolder\\outputfile.txt', read, Mf), read_file(Mf,X), close(Mf)),
                         error(Err,Context),
                         open_output_aux(Err,X)). % AUX PROCEDURE TO RECOVER

% Write some code here
% open_output_aux code matches the error code with 'existence_error';
% if true, calls open_aux_wait procedure; else throw the error.

open_aux_wait(Z):- catch((open('C:\\myfolder\\outputfile.txt', read, Mf), read_file(Mf,Z), close(Mf)),
                   error(Err,Context),
                   open_aux_wait(Z)).

However this seems to be very inefficient of way doing this. I wanted to know if there's any better way to do this, like in java, you could simply call wait() while handling file exception. In the documentation, there's a method 'wait_for_input\3' but it says 'wait_for_input()' cannot be used for File Streams in Windows. I tried using it, but it gives error.

Any help or guidelines is greatly appreciated.

shujin
  • 181
  • 12

1 Answers1

2

You have several options to solve this:

  1. You can totally reconsider the way that these processes communicate. For example, SWI-Prolog ships with very powerful HTTP libraries, and you can set up the communication between the two processes to work over HTTP, using a client/server architecture. In this case, you avoid the busy waiting that currently uses many CPU cycles unnecessarily.

  2. A much more trivial solution is to simply insert a call of the built-in sleep/1 predicate to put the process to sleep before trying to open the file again. Use for example sleep(1) to put the process to sleep for 1 second.

  3. Use for example process_wait/2 to wait until the called process has finished and generated the file.

From what you are describing, it looks like (2) will do, (3) is slightly more elegant, and (1) may be good to know in general, for more difficult cases.

mat
  • 40,498
  • 3
  • 51
  • 78
  • Thank you for your suggestions. Both of them are good, however I did not want to use sleep/1 initially, since it messes up my statistics/2, as I want to calculate the time the whole procedure took. Also, the other application, sometimes outputs in secs, while at other times even takes hours. So I was concerned if my code could crash. I understand techniques like just polling a bit, or interrupts cannot work here. – shujin Jul 01 '16 at 07:21
  • I have added method (3), which I hope is more suitable in your scenario: Use `process_wait/2` to wait until the called process finishes, and then read the file. You may have to slightly change the way the process is invoked to do this. Also note that you can use for example `sleep(0.1)`, significantly reducing the CPU load and retaining more acceptable performance. – mat Jul 01 '16 at 07:25
  • But I was thinking along the lines of something like 1) my code calls another win_shell/3 or win_exec/2, (a parallel task) which runs some script that checks the existence of the outputfile and returns status to code. Or, 2) Instead of calling win_exec to call the other application directly, I use win_exec to call 'cmd.exe /K external_app.bat' and let cmd inform me of the status of the outputfile existence. I am not aware of any such cmd switches that does the job. Hoping code doesn't crash due to excess cpu cycles, there should not be stack overflow as open_aux_wait should act tail-recursive. – shujin Jul 01 '16 at 07:33
  • For better portability and to simplify the interaction of the involved processes, I recommend you simply start the external process with [`process_create/3`](http://eu.swi-prolog.org/pldoc/doc_for?object=process_create/3), using the `process(PID)` option, and then use `process_wait/2` to wait on the `PID` until it is finished. This is simple and portable also to other platforms. – mat Jul 01 '16 at 07:36
  • Thank you for your response. Can you please give me a code example to use process_create/3? When I used it to open an application like Notepad.exe, the notepad opens. However, it doesn't work for applications that use command prompt. e.g. I tried: `main :- process_create('C:\\WINDOWS\\system32\\cmd.exe',[] ,[])` which gives an `ERROR: Process "c:\windows\system32\cmd.exe": exit status: 1`. – shujin Jul 01 '16 at 10:12
  • 1
    You should probably turn this into a question of its own. At first glance, I can imagine that you need to specify `[stdin(pipe(Stream))]` as the third argument, so that you can pass further input to the process. – mat Jul 01 '16 at 11:03