Added thread-safe environment functions

Also marked the existing functions as unsafe, as they can cause crashes if used in multi-threaded applications.

As a bonus, since the new functions are hashtable based, hint environment lookups are much faster.
This commit is contained in:
Sam Lantinga
2024-09-13 17:00:15 -07:00
parent 16ff7503b7
commit 90e01040c5
49 changed files with 696 additions and 252 deletions

View File

@@ -5,14 +5,6 @@
#include <stdio.h>
#include <errno.h>
#if defined(SDL_PLATFORM_WINDOWS)
#include <windows.h>
#elif defined(SDL_PLATFORM_MACOS)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif
int main(int argc, char *argv[]) {
SDLTest_CommonState *state;
@@ -95,26 +87,18 @@ int main(int argc, char *argv[]) {
}
if (print_environment || expect_environment) {
#if defined(SDL_PLATFORM_WINDOWS)
char *original_env = GetEnvironmentStrings();
const char *env = original_env;
for (; env[0]; env += SDL_strlen(env) + 1) {
#else
char **envp = environ;
for (; *envp; envp++) {
const char *env = *envp;
#endif
if (print_environment) {
fprintf(stdout, "%s\n", env);
}
if (expect_environment) {
expect_environment_match |= SDL_strcmp(env, expect_environment) == 0;
char **env = SDL_GetEnvironmentVariables(SDL_GetEnvironment());
if (env) {
for (i = 0; env[i]; ++i) {
if (print_environment) {
fprintf(stdout, "%s\n", env[i]);
}
if (expect_environment) {
expect_environment_match |= SDL_strcmp(env[i], expect_environment) == 0;
}
}
SDL_free(env);
}
#ifdef SDL_PLATFORM_WINDOWS
FreeEnvironmentStringsA(original_env);
#endif
}
if (stdin_to_stdout || stdin_to_stderr) {

View File

@@ -740,7 +740,7 @@ int main(int argc, char *argv[])
RunBasicTest();
if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
SDL_Log("Not running slower tests");
return 0;
}

View File

@@ -130,7 +130,7 @@ static int SDLCALL hints_setHint(void *arg)
SDL_free(value);
/* Set default value in environment */
SDL_setenv(testHint, "original", 1);
SDL_SetEnvironmentVariable(SDL_GetEnvironment(), testHint, "original", 1);
SDLTest_AssertPass("Call to SDL_GetHint() after saving and restoring hint");
originalValue = SDL_GetHint(testHint);

View File

@@ -542,10 +542,11 @@ static int SDLCALL stdlib_swprintf(void *arg)
#endif
/**
* Call to SDL_getenv and SDL_setenv
* Call to SDL_GetEnvironmentVariable() and SDL_SetEnvironmentVariable()
*/
static int SDLCALL stdlib_getsetenv(void *arg)
{
SDL_Environment *env = SDL_GetEnvironment();
const int nameLen = 16;
char name[17];
int counter;
@@ -556,7 +557,7 @@ static int SDLCALL stdlib_getsetenv(void *arg)
int overwrite;
const char *text;
/* Create a random name. This tests SDL_getenv, since we need to */
/* Create a random name. This tests SDL_GetEnvironmentVariable, since we need to */
/* make sure the variable is not set yet (it shouldn't). */
do {
for (counter = 0; counter < nameLen; counter++) {
@@ -564,8 +565,8 @@ static int SDLCALL stdlib_getsetenv(void *arg)
}
name[nameLen] = '\0';
text = SDL_getenv(name);
SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
text = SDL_GetEnvironmentVariable(env, name);
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
if (text) {
SDLTest_Log("Expected: NULL, Got: '%s' (%i)", text, (int)SDL_strlen(text));
}
@@ -578,13 +579,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
/* Set value 1 without overwrite */
overwrite = 0;
expected = value1;
result = SDL_setenv(name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
/* Check value */
text = SDL_getenv(name);
SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
text = SDL_GetEnvironmentVariable(env, name);
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
if (text != NULL) {
SDLTest_AssertCheck(
@@ -597,13 +598,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
/* Set value 2 with overwrite */
overwrite = 1;
expected = value2;
result = SDL_setenv(name, value2, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value2, overwrite);
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, name, value2, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value2, overwrite);
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
/* Check value */
text = SDL_getenv(name);
SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
text = SDL_GetEnvironmentVariable(env, name);
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
if (text != NULL) {
SDLTest_AssertCheck(
@@ -616,13 +617,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
/* Set value 1 without overwrite */
overwrite = 0;
expected = value2;
result = SDL_setenv(name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
/* Check value */
text = SDL_getenv(name);
SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
text = SDL_GetEnvironmentVariable(env, name);
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
if (text != NULL) {
SDLTest_AssertCheck(
@@ -635,13 +636,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
/* Set value 1 with overwrite */
overwrite = 1;
expected = value1;
result = SDL_setenv(name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
/* Check value */
text = SDL_getenv(name);
SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
text = SDL_GetEnvironmentVariable(env, name);
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
if (text != NULL) {
SDLTest_AssertCheck(
@@ -652,48 +653,48 @@ static int SDLCALL stdlib_getsetenv(void *arg)
}
/* Verify setenv() with empty string vs unsetenv() */
result = SDL_setenv("FOO", "1", 1);
SDLTest_AssertPass("Call to SDL_setenv('FOO','1', 1)");
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, "FOO", "1", 1);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','1', 1)");
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
expected = "1";
text = SDL_getenv("FOO");
SDLTest_AssertPass("Call to SDL_getenv('FOO')");
text = SDL_GetEnvironmentVariable(env, "FOO");
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
result = SDL_setenv("FOO", "", 1);
SDLTest_AssertPass("Call to SDL_setenv('FOO','', 1)");
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, "FOO", "", 1);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','', 1)");
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
expected = "";
text = SDL_getenv("FOO");
SDLTest_AssertPass("Call to SDL_getenv('FOO')");
text = SDL_GetEnvironmentVariable(env, "FOO");
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: '%s', got: '%s'", expected, text);
result = SDL_unsetenv("FOO");
SDLTest_AssertPass("Call to SDL_unsetenv('FOO')");
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
text = SDL_getenv("FOO");
SDLTest_AssertPass("Call to SDL_getenv('FOO')");
result = SDL_UnsetEnvironmentVariable(env, "FOO");
SDLTest_AssertPass("Call to SDL_UnsetEnvironmentVariable(env, 'FOO')");
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
text = SDL_GetEnvironmentVariable(env, "FOO");
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
SDLTest_AssertCheck(text == NULL, "Verify returned text, expected: (null), got: %s", text);
result = SDL_setenv("FOO", "0", 0);
SDLTest_AssertPass("Call to SDL_setenv('FOO','0', 0)");
SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, "FOO", "0", 0);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','0', 0)");
SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
expected = "0";
text = SDL_getenv("FOO");
SDLTest_AssertPass("Call to SDL_getenv('FOO')");
text = SDL_GetEnvironmentVariable(env, "FOO");
SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
/* Negative cases */
for (overwrite = 0; overwrite <= 1; overwrite++) {
result = SDL_setenv(NULL, value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv(NULL,'%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
result = SDL_setenv("", value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('','%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
result = SDL_setenv("=", value1, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('=','%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
result = SDL_setenv(name, NULL, overwrite);
SDLTest_AssertPass("Call to SDL_setenv('%s', NULL, %i)", name, overwrite);
SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
result = SDL_SetEnvironmentVariable(env, NULL, value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, NULL,'%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, "", value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '','%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, "=", value1, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '=','%s', %i)", value1, overwrite);
SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
result = SDL_SetEnvironmentVariable(env, name, NULL, overwrite);
SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s', NULL, %i)", name, overwrite);
SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
}
/* Clean up */
@@ -1402,7 +1403,7 @@ static const SDLTest_TestCaseReference stdlibTest_swprintf = {
};
static const SDLTest_TestCaseReference stdlibTest_getsetenv = {
stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED
stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_GetEnvironmentVariable and SDL_SetEnvironmentVariable", TEST_ENABLED
};
static const SDLTest_TestCaseReference stdlibTest_sscanf = {

View File

@@ -43,7 +43,7 @@ static SDL_Window *createVideoSuiteTestWindow(const char *title)
needs_renderer = SDL_TRUE;
} else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
/* Try to detect if the x11 driver is running under XWayland */
const char *session_type = SDL_getenv("XDG_SESSION_TYPE");
const char *session_type = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
if (session_type && SDL_strcasecmp(session_type, "wayland") == 0) {
needs_renderer = SDL_TRUE;
}
@@ -1929,7 +1929,7 @@ static int SDLCALL video_getSetWindowState(void *arg)
* Other desktops can be enabled in the future as required.
*/
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0 || SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
const char *desktop = SDL_getenv("XDG_CURRENT_DESKTOP");
const char *desktop = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CURRENT_DESKTOP");
if (SDL_strcmp(desktop, "GNOME") != 0 && SDL_strcmp(desktop, "KDE") != 0) {
SDLTest_Log("Skipping test video_getSetWindowState: desktop environment %s not supported", desktop);
return TEST_SKIPPED;

View File

@@ -93,7 +93,7 @@ int main(int argc, char *argv[])
/* Set the error value for the main thread */
SDL_SetError("No worries");
if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
SDL_Log("Not running slower tests");
SDL_Quit();
return 0;

View File

@@ -35,39 +35,35 @@ static void SDLCALL setUpProcess(void **arg) {
static const char *options[] = { "/path/to/childprocess" EXE, NULL };
static char *env_key_val_string(const char *key) {
const char *env = SDL_getenv(key);
size_t size_result;
char *result;
if (env == NULL) {
return NULL;
}
size_result = SDL_strlen(key) + SDL_strlen(env) + 2;
result = SDL_malloc(size_result);
SDL_snprintf(result, size_result, "%s=%s", key, env);
return result;
}
static char **DuplicateEnvironment(const char *key0, ...)
{
va_list ap;
size_t count = 1;
size_t i;
const char *keyN;
SDL_Environment *env = SDL_GetEnvironment();
SDL_Environment *new_env = SDL_CreateEnvironment(SDL_TRUE);
char **result;
if (key0) {
if (SDL_strchr(key0, '=') || SDL_getenv(key0)) {
count += 1;
char *sep = SDL_strchr(key0, '=');
if (sep) {
*sep = '\0';
SDL_SetEnvironmentVariable(new_env, key0, sep + 1, SDL_TRUE);
*sep = '=';
SDL_SetEnvironmentVariable(new_env, key0, sep, SDL_TRUE);
} else {
SDL_SetEnvironmentVariable(new_env, key0, SDL_GetEnvironmentVariable(env, key0), SDL_TRUE);
}
va_start(ap, key0);
for (;;) {
keyN = va_arg(ap, const char *);
if (keyN) {
if (SDL_strchr(keyN, '=') || SDL_getenv(keyN)) {
count += 1;
sep = SDL_strchr(keyN, '=');
if (sep) {
*sep = '\0';
SDL_SetEnvironmentVariable(new_env, keyN, sep + 1, SDL_TRUE);
*sep = '=';
} else {
SDL_SetEnvironmentVariable(new_env, keyN, SDL_GetEnvironmentVariable(env, keyN), SDL_TRUE);
}
} else {
break;
@@ -76,45 +72,11 @@ static char **DuplicateEnvironment(const char *key0, ...)
va_end(ap);
}
result = SDL_calloc(count, sizeof(char *));
i = 0;
if (key0) {
if (SDL_strchr(key0, '=')) {
result[i++] = SDL_strdup(key0);
} else if (SDL_getenv(key0)) {
result[i++] = env_key_val_string(key0);
}
va_start(ap, key0);
for (;;) {
keyN = va_arg(ap, const char *);
if (keyN) {
if (SDL_strchr(keyN, '=')) {
result[i++] = SDL_strdup(keyN);
} else if (SDL_getenv(keyN)) {
result[i++] = env_key_val_string(keyN);
}
} else {
break;
}
}
va_end(ap);
}
result = SDL_GetEnvironmentVariables(new_env);
SDL_DestroyEnvironment(new_env);
return result;
}
static void DestroyEnvironment(char **environment) {
char **envp;
if (!environment) {
return;
}
for (envp = environment; *envp; envp++) {
SDL_free(*envp);
}
SDL_free(environment);
}
static int SDLCALL process_testArguments(void *arg)
{
TestProcessData *data = (TestProcessData *)arg;
@@ -187,7 +149,7 @@ static int SDLCALL process_testInheritedEnv(void *arg)
test_env_val = SDLTest_RandomAsciiStringOfSize(32);
SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY, test_env_val);
SDL_setenv(TEST_ENV_KEY, test_env_val, 1);
SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY, test_env_val, SDL_TRUE);
SDL_snprintf(buffer, sizeof(buffer), "%s=%s", TEST_ENV_KEY, test_env_val);
process_args[3] = buffer;
@@ -312,13 +274,14 @@ static int SDLCALL process_testNewEnv(void *arg)
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
SDLTest_AssertPass("About to destroy process");
SDL_DestroyProcess(process);
DestroyEnvironment(process_env);
SDL_free(process_env);
SDL_free(test_env_val);
return TEST_COMPLETED;
failed:
SDL_free(test_env_val);
SDL_DestroyProcess(process);
DestroyEnvironment(process_env);
SDL_free(process_env);
return TEST_ABORTED;
}
@@ -330,7 +293,6 @@ static int process_testStdinToStdout(void *arg)
"--stdin-to-stdout",
NULL,
};
const char **process_env = NULL;
SDL_PropertiesID props;
SDL_Process *process = NULL;
Sint64 pid;
@@ -346,7 +308,6 @@ static int process_testStdinToStdout(void *arg)
props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, (void *)process_env);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
process = SDL_CreateProcessWithProperties(props);

View File

@@ -223,7 +223,7 @@ int main(int argc, char *argv[])
SDL_Log("Playing %d Hz test tone on channel: %s\n", sine_freq, get_channel_name(j, total_channels));
/* fill_buffer() will increment the active channel */
if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
SDL_Delay(QUICK_TEST_TIME_MSEC);
} else {
SDL_Delay(CHANNEL_TEST_TIME_SEC * 1000);

View File

@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
return 1;
}
if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
SDL_Log("Not running slower tests");
SDL_Quit();
return 0;

View File

@@ -124,7 +124,7 @@ int main(int argc, char *argv[])
return 1;
}
if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
SDL_Log("Not running slower tests");
SDL_Quit();
return 0;