This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of TS status.

### 2653. [filesys.ts] [PDTS] `directory_entry` multithreading concerns

Section: 12 [filesys.ts::class.directory_entry] Status: TS Submitter: Stephan T. Lavavej Opened: 2014-02-03 Last modified: 2017-07-30

Priority: Not Prioritized

View all other issues in [filesys.ts::class.directory_entry].

View all issues with TS status.

Discussion:

Addresses: filesys.ts

12 [class.directory_entry] depicts `directory_entry` as having `mutable m_status` and `m_symlink_status` data members. This is problematic because of C++11's multithreading guarantees, which say that `const` member functions are simultaneously callable. As a result, `mutable` data members must be protected with synchronization, which was probably not considered during this design. The complexity and expense may be acceptable in `directory_entry` (since it can save filesystem queries, apparently) but it would be very very nice to have a note about this.

[2014-02-13 LWG/SG-3 discussed in Issaquah: Beman and STL will work together to better understand the concerns, and to formulate a solution. This is not a blocking issue for finishing the TS, but is a blocking issue for merging into the IS]

[2014-04-17 Beman provides proposed wording]

[2014-06-19 Rapperswil LWG decides to remove mutable private members. Beman provides wording.]

Proposed resolution:

Change 12 [class.directory_entry] as indicated:

        namespace std { namespace experimental { namespace filesystem { inline namespace v1 {

        class directory_entry
        {
        public:

        // constructors and destructor
        directory_entry() = default;
        directory_entry(const directory_entry&) = default;
        directory_entry(directory_entry&&) noexcept = default;
        explicit directory_entry(const path& p);
        explicit directory_entry(const path& p, file_status st=file_status(),
          file_status symlink_st=file_status());
        ~directory_entry();

        // modifiers
        directory_entry& operator=(const directory_entry&) = default;
        directory_entry& operator=(directory_entry&&) noexcept = default;
        void assign(const path& p);
        void assign(const path& p, file_status st=file_status(),
          file_status symlink_st=file_status());
        void replace_filename(const path& p);
        void replace_filename(const path& p, file_status st=file_status(),
          file_status symlink_st=file_status());

        // observers
        const path&  path() const noexcept;
        file_status  status() const;
        file_status  status(error_code& ec) const noexcept;
        file_status  symlink_status() const;
        file_status  symlink_status(error_code& ec) const noexcept;

        bool operator< (const directory_entry& rhs) const noexcept;
        bool operator==(const directory_entry& rhs) const noexcept;
        bool operator!=(const directory_entry& rhs) const noexcept;
        bool operator<=(const directory_entry& rhs) const noexcept;
        bool operator> (const directory_entry& rhs) const noexcept;
        bool operator>=(const directory_entry& rhs) const noexcept;
        private:
        path                 m_path;           // for exposition only
        mutable file_status  m_status;         // for exposition only; stat()-like
        mutable file_status  m_symlink_status; // for exposition only; lstat()-like
        };

        } } } }  // namespaces std::experimental::filesystem::v1

A `directory_entry` object stores a `path` object, a `file_status` object for non-symbolic link status, and a `file_status` object for symbolic link status. The `file_status` objects act as value caches.

> [Note: Because `status()`on a pathname may be a relatively expensive operation, some operating systems provide status information as a byproduct of directory iteration. Caching such status information can result in significant time savings. Cached and non-cached results may differ in the presence of file system races. —end note]

### 12.1 `directory_entry` constructors [directory_entry.cons]

Add the description of this constructor in its entirety:

    explicit directory_entry(const path& p);

> Effects: Constructs an object of type `directory_entry`

> Postcondition: `path() == p`.

    explicit directory_entry(const path& p, file_status st=file_status(),
        file_status symlink_st=file_status());

> Strike the description

### 12.2 `directory_entry` modifiers [directory_entry.mods]

Add the description of this function in its entirety:

    void assign(const path& p);

> Postcondition: `path() == p`.

    void assign(const path& p, file_status st=file_status(),
      file_status symlink_st=file_status());

> Strike the description

Add the description of this function in its entirety:

    void replace_filename(const path& p);

> Postcondition: `path() == x.parent_path() / p` where `x` is the value of `path()` before the function is called.

    void replace_filename(const path& p, file_status st=file_status(),
      file_status symlink_st=file_status());

> Strike the description

### 12.3 `directory_entry` observers [directory_entry.obs]

    const path& path() const noexcept;

> Returns: `m_path`

    file_status status() const;
    file_status status(error_code& ec) const noexcept;

> Effects: As if,

> >     if (!status_known(m_status))
> >     {
> >       if (status_known(m_symlink_status) && !is_symlink(m_symlink_status))
> >         { m_status = m_symlink_status; }
> >       else { m_status = status(m_path[, ec]); }
> >     }

> Returns: `m_status status(path()[, ec])` .

> Throws: As specified in Error reporting (7).

    file_status  symlink_status() const;
    file_status  symlink_status(error_code& ec) const noexcept;

> Effects: As if,

> >             if (!status_known(m_symlink_status))
> >             {
> >             m_symlink_status = symlink_status(m_path[, ec]);
> >             }

> Returns: `m_symlink_status symlink_status(path()[, ec])` .

> Throws: As specified in Error reporting (7).

    bool operator==(const directory_entry& rhs) const noexcept;

> Returns: `m_path == rhs.m_path`.

> [Note: Status members do not participate in determining equality. — end note]

    bool operator!=(const directory_entry& rhs) const noexcept;

> Returns: `m_path != rhs.m_path`.

    bool operator< (const directory_entry& rhs) const noexcept;

> Returns: `m_path < rhs.m_path`.

    bool operator<=(const directory_entry& rhs) const noexcept;

> Returns: `m_path <= rhs.m_path`.

    bool operator> (const directory_entry& rhs) const noexcept;

> Returns: `m_path > rhs.m_path`.

    bool operator>=(const directory_entry& rhs) const noexcept;

> Returns: `m_path >= rhs.m_path`.