Ghost: Make special user directory query thread safe

Similar to e0ff7731e0.

Noticed a data race when working on blender/blender!130543, which calls
this function from a thread. Make this thread safe by avoiding returning
of static memory, instead use an optional `std::string`.

Pull Request: https://projects.blender.org/blender/blender/pulls/141083
This commit is contained in:
Julian Eisel
2025-06-27 15:16:35 +02:00
committed by Julian Eisel
parent 52caedb19e
commit 4b2d60a2c3
10 changed files with 71 additions and 37 deletions

View File

@@ -8,6 +8,9 @@
#pragma once
#include <optional>
#include <string>
#include "GHOST_Types.h"
class GHOST_ISystemPaths {
@@ -60,9 +63,9 @@ class GHOST_ISystemPaths {
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user directory (eg `~/Documents/`).
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
*/
virtual const char *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
virtual std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
/**
* Determine the directory of the current binary

View File

@@ -8,6 +8,9 @@
#pragma once
#include <optional>
#include <string>
#include "GHOST_Types.h"
GHOST_DECLARE_HANDLE(GHOST_SystemPathsHandle);
@@ -46,9 +49,9 @@ extern const char *GHOST_getUserDir(int version, const char *versionstr);
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user directory (eg `~/Documents/`).
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
*/
extern const char *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type);
extern std::optional<std::string> GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type);
/**
* Determine the directory in which the binary file is found.

View File

@@ -34,7 +34,7 @@ const char *GHOST_getUserDir(int version, const char *versionstr)
return systemPaths ? systemPaths->getUserDir(version, versionstr) : nullptr;
}
const char *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
std::optional<std::string> GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
{
const GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
/* Shouldn't be `nullptr`. */

View File

@@ -12,6 +12,9 @@
# error Apple OSX only!
#endif // __APPLE__
#include <optional>
#include <string>
#include "GHOST_SystemPaths.hh"
class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
@@ -42,9 +45,9 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user directory (eg `~/Documents/`).
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
*/
const char *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
/**
* Determine the directory of the current binary.

View File

@@ -5,6 +5,9 @@
#import <AppKit/NSDocumentController.h>
#import <Foundation/Foundation.h>
#include <optional>
#include <string>
#include "GHOST_Debug.hh"
#include "GHOST_SystemPathsCocoa.hh"
@@ -46,9 +49,10 @@ const char *GHOST_SystemPathsCocoa::getUserDir(int /* version */, const char *ve
return GetApplicationSupportDir(versionstr, NSUserDomainMask, tempPath, sizeof(tempPath));
}
const char *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
std::optional<std::string> GHOST_SystemPathsCocoa::getUserSpecialDir(
GHOST_TUserSpecialDirTypes type) const
{
static char tempPath[512] = "";
char tempPath[512] = "";
@autoreleasepool {
NSSearchPathDirectory ns_directory;
@@ -78,12 +82,12 @@ const char *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes
GHOST_ASSERT(
false,
"GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter");
return nullptr;
return std::nullopt;
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES);
if (paths.count == 0) {
return nullptr;
return std::nullopt;
}
NSString *basePath = [paths objectAtIndex:0];
@@ -94,6 +98,9 @@ const char *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes
memcpy(tempPath, basePath_cstr, basePath_len);
tempPath[basePath_len] = '\0';
}
if (!tempPath[0]) {
return std::nullopt;
}
return tempPath;
}

View File

@@ -6,6 +6,7 @@
* \ingroup GHOST
*/
#include <optional>
#include <sstream>
#include "GHOST_SystemPathsUnix.hh"
@@ -100,10 +101,10 @@ const char *GHOST_SystemPathsUnix::getUserDir(int version, const char *versionst
return user_path.c_str();
}
const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
std::optional<std::string> GHOST_SystemPathsUnix::getUserSpecialDir(
GHOST_TUserSpecialDirTypes type) const
{
const char *type_str;
static string path;
switch (type) {
case GHOST_kUserSpecialDirDesktop:
@@ -133,16 +134,15 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes
/* If `XDG_CACHE_HOME` is not set, then `$HOME/.cache is used`. */
const char *home_dir = home_dir_get();
if (home_dir == nullptr) {
return nullptr;
return std::nullopt;
}
path = string(home_dir) + "/.cache";
return path.c_str();
return string(home_dir) + "/.cache";
}
default:
GHOST_ASSERT(
false,
"GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter");
return nullptr;
return std::nullopt;
}
/* Pipe `stderr` to `/dev/null` to avoid error prints. We will fail gracefully still. */
@@ -150,7 +150,7 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes
FILE *fstream = popen(command.c_str(), "r");
if (fstream == nullptr) {
return nullptr;
return std::nullopt;
}
std::stringstream path_stream;
while (!feof(fstream)) {
@@ -163,11 +163,11 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes
}
if (pclose(fstream) == -1) {
perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()");
return nullptr;
return std::nullopt;
}
path = path_stream.str();
return path[0] ? path.c_str() : nullptr;
std::string path = path_stream.str();
return path[0] ? std::optional(path) : std::nullopt;
}
const char *GHOST_SystemPathsUnix::getBinaryDir() const

View File

@@ -8,6 +8,9 @@
#pragma once
#include <optional>
#include <string>
#include "../GHOST_Types.h"
#include "GHOST_SystemPaths.hh"
@@ -40,9 +43,9 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user directory (eg `~/Documents/`).
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
*/
const char *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
/**
* Determine the directory of the current binary.

View File

@@ -62,7 +62,8 @@ const char *GHOST_SystemPathsWin32::getUserDir(int, const char *versionstr) cons
return user_dir;
}
const char *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
std::optional<std::string> GHOST_SystemPathsWin32::getUserSpecialDir(
GHOST_TUserSpecialDirTypes type) const
{
const char *special_dir = nullptr;
@@ -93,7 +94,7 @@ const char *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes
GHOST_ASSERT(
false,
"GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter");
return nullptr;
return std::nullopt;
}
static char knownpath[MAX_PATH * 3] = {0};
@@ -105,6 +106,10 @@ const char *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes
special_dir = knownpath;
}
if ((special_dir == nullptr) || (special_dir[0] == '\0')) {
return std::nullopt;
}
CoTaskMemFree(knownpath_16);
return special_dir;
}

View File

@@ -8,6 +8,9 @@
#pragma once
#include <optional>
#include <string>
#ifndef WIN32
# error WIN32 only!
#endif // WIN32
@@ -49,9 +52,9 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user directory (eg `~/Documents/`).
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
*/
const char *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
/**
* Determine the directory of the current binary.