3

If I chdir within a thread, will that affect the cwd of the parent program?

nes1983
  • 15,209
  • 4
  • 44
  • 64
porque_no_les_deux
  • 479
  • 1
  • 6
  • 18

3 Answers3

8

Yes.

If you need relative paths in a multithreaded application, it's safest to use the at() versions of functions. For example, openat() is like open():

int openat(int dirfd, const char *pathname, int flags);

The first parameter is the fd to a directory. The path is relative to that directory.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Then what's the alternative for copying a file up a directory? I don't want to do an absolute path for the write() call. If chdir'ing isn't thread-safe, and I have a number of threads copying files from one folder to its parent folder, how can this be done? – porque_no_les_deux Nov 18 '12 at 09:25
  • On recent POSIX systems, you can use the "at" functions: `openat()`, for example. They let you work with paths relative to a directory. You open the directory as a file descriptor, then you can open files relative to it. – Dietrich Epp Nov 18 '12 at 09:27
  • This is a very helpful answer except than I am sufficiently New2This that I do not know how to create a directory file descriptor. Nor can I find any internet examples for it... – porque_no_les_deux Nov 18 '12 at 09:30
  • @New2This: It's a regular file descriptor. You use `open()` or `openat()`, just like with files. (You can't read from the descriptor, though.) – Dietrich Epp Nov 18 '12 at 10:18
1

Yes, the "parent program" (initial thread of the thread's process) will be affected because the current directory is shared by all threads of a process.

http://linux.die.net/man/7/pthreads

Peter G.
  • 14,786
  • 7
  • 57
  • 75
1

On both Linux and macOS, there is a per-thread current directory which can override the process-wide current directory. It is not set by default, and the APIs to set it are completely non-portable (not standardised by anything).

On Linux, your thread needs to call unshare(CLONE_FS) to make its filesystem information (root directory, current directory and umask) independent of the rest of the process. Having done that, subsequent calls to chdir (and also chroot and umask) will impact that thread only, and not the other threads in the process. Note this is irreversible: once the thread's filesystem information is disconnected from the process, there is no way to reconnect it – keep that in mind if you use thread pools.

On macOS, there are two undocumented APIs, pthread_chdir_np and pthread_fchdir_np, which are like chdir and fchdir, just per-thread rather than per-process. These are not in the public headers, but you can copy their declarations from Apple's open source libpthread source code's private/pthread/private.h:

__API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0))
int pthread_chdir_np(const char *path);

__API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0))
int pthread_fchdir_np(int fd);

Note that, unlike Linux, this actually can be reversed by calling pthread_fchdir_np(-1), which will clear out the per-thread current directory and go back to inheriting that of the process – if you are using a thread pool (such as libdispatch), cleaning up after yourself by calling pthread_fchdir_np(-1) is very important, otherwise you may encounter hard-to-diagnose bugs (some thread randomly having an unexpected current directory).

As undocumented interfaces, Apple could in theory discontinue (or incompatibly change) them at any time. However, given that Google Chrome (among other apps) is using these functions, I'd be rather surprised if Apple were ever to do that.

Also, as the Chromium source mentions, while these pthread library calls were only added in 10.12, they are thin wrappers over the __pthread_chdir and __pthread_fchdir system calls which have existed since 10.5. If (for some reason) your code still needs to support macOS 10.5–10.11, you could do so by calling those system calls directly.

I'm not aware of any other common platforms having a per-thread current working directory. In particular, there is no such concept on Windows. One should avoid doing this (use openat for example) unless one has no other choice.

Simon Kissane
  • 4,373
  • 3
  • 34
  • 59