ocamlocaml-lwt

How to correctly start a process from a specific directory with Lwt


It is easy to start a process from a specific directory with Lwt using the functions Sys.getpwd, Lwt_unix.chdir and Lwt_process.exec:

  1. Use Sys.getpwd to save the current working directory
  2. Use Lwt_unix.chdir to change to the specific directory
  3. Use Lwt_process.exec to start the external process
  4. Use Lwt_unix.chdir to change to the saved current working directory

This logic is flawed, for it allows the scheduler to run another thread after after the first call to Lwt_unix.chdir and after the call to Lwt_process.exec which would lead this thread to be run in special directory rather than in the saved current directory. Is it possible to easily start a process from a special directory with Lwt without introducing a race condition such as the one I describe?


Solution

  • You can protect your current working directory with with some synchronization primitive, like Lwt_mutex. But there is some caveat here, suppose you have this chain:

    lock dir_guard >> chdir dir >> exec proc >> chdir dir' >> unlock dir_guard
    

    Which disallows changing the directory for the whole time the process proc is performing its task. This maybe overcautious and unnecessary. The following code doesn't have this problems:

    let exec_in_folder guard dir proc = 
      with_lock guard (fun () -> 
         chdir dir >>= fun () -> return (exec proc)) >>= fun proc_t ->
      proc_t
    

    But, this code, has an issue, it is correct only if process is started atomically, i.e., if there is no such possibility that during the process starting procedure there will be some rescheduling, that will allow other thread to interfere and to change current folder. To proof that it is atomic, you can either read sources, or implement your own process started, that will have such guarantees. If you will read the code, then you will figure out, that process is created with spawn function, that momentary will do a fork without any interspersed threads. So yes, this code is correct.