unix: make get(set)_process_title MT-safe

Used a shared uv_mutex_t in unix implementations of these functions
to prevent simultaneous execution.

Fixes: https://github.com/libuv/libuv/issues/271
PR-URL: https://github.com/libuv/libuv/pull/1640
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
Matt Harrison 2017-11-15 15:31:01 +07:00 committed by Santiago Gimeno
parent 0d6525acae
commit 8a6d1b32c5
11 changed files with 215 additions and 16 deletions

View File

@ -222,6 +222,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-poll-closesocket.c \ test/test-poll-closesocket.c \
test/test-poll-oob.c \ test/test-poll-oob.c \
test/test-process-title.c \ test/test-process-title.c \
test/test-process-title-threadsafe.c \
test/test-queue-foreach-delete.c \ test/test-queue-foreach-delete.c \
test/test-ref.c \ test/test-ref.c \
test/test-run-nowait.c \ test/test-run-nowait.c \

View File

@ -128,6 +128,7 @@ test/test-platform-output.c
test/test-poll-close.c test/test-poll-close.c
test/test-poll.c test/test-poll.c
test/test-process-title.c test/test-process-title.c
test/test-process-title-threadsafe.c
test/test-ref.c test/test-ref.c
test/test-run-nowait.c test/test-run-nowait.c
test/test-run-once.c test/test-run-once.c

View File

@ -197,8 +197,7 @@ API
`UV_EINVAL` is returned. If `size` cannot accommodate the process title and `UV_EINVAL` is returned. If `size` cannot accommodate the process title and
terminating `NULL` character, the function returns `UV_ENOBUFS`. terminating `NULL` character, the function returns `UV_ENOBUFS`.
.. warning:: .. versionchanged:: 1.18.1 now thread-safe on all supported platforms.
`uv_get_process_title` is not thread safe on any platform except Windows.
.. c:function:: int uv_set_process_title(const char* title) .. c:function:: int uv_set_process_title(const char* title)
@ -208,8 +207,7 @@ API
larger than the available space. Other platforms will return `UV_ENOMEM` if larger than the available space. Other platforms will return `UV_ENOMEM` if
they cannot allocate enough space to duplicate the contents of `title`. they cannot allocate enough space to duplicate the contents of `title`.
.. warning:: .. versionchanged:: 1.18.1 now thread-safe on all supported platforms.
`uv_set_process_title` is not thread safe on any platform except Windows.
.. c:function:: int uv_resident_set_memory(size_t* rss) .. c:function:: int uv_resident_set_memory(size_t* rss)

View File

@ -65,11 +65,18 @@
#define RDWR_BUF_SIZE 4096 #define RDWR_BUF_SIZE 4096
#define EQ(a,b) (strcmp(a,b) == 0) #define EQ(a,b) (strcmp(a,b) == 0)
static uv_mutex_t process_title_mutex;
static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
static void* args_mem = NULL; static void* args_mem = NULL;
static char** process_argv = NULL; static char** process_argv = NULL;
static int process_argc = 0; static int process_argc = 0;
static char* process_title_ptr = NULL; static char* process_title_ptr = NULL;
static void init_process_title_mutex_once(void) {
uv_mutex_init(&process_title_mutex);
}
int uv__platform_loop_init(uv_loop_t* loop) { int uv__platform_loop_init(uv_loop_t* loop) {
loop->fs_fd = -1; loop->fs_fd = -1;
@ -856,6 +863,9 @@ int uv_set_process_title(const char* title) {
if (new_title == NULL) if (new_title == NULL)
return -ENOMEM; return -ENOMEM;
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
/* If this is the first time this is set, /* If this is the first time this is set,
* don't free and set argv[1] to NULL. * don't free and set argv[1] to NULL.
*/ */
@ -868,6 +878,8 @@ int uv_set_process_title(const char* title) {
if (process_argc > 1) if (process_argc > 1)
process_argv[1] = NULL; process_argv[1] = NULL;
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }
@ -880,8 +892,13 @@ int uv_get_process_title(char* buffer, size_t size) {
else if (size <= len) else if (size <= len)
return -ENOBUFS; return -ENOBUFS;
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
memcpy(buffer, process_argv[0], len + 1); memcpy(buffer, process_argv[0], len + 1);
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }

View File

@ -47,9 +47,16 @@
# define CP_INTR 4 # define CP_INTR 4
#endif #endif
static uv_mutex_t process_title_mutex;
static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
static char *process_title; static char *process_title;
static void init_process_title_mutex_once(void) {
uv_mutex_init(&process_title_mutex);
}
int uv__platform_loop_init(uv_loop_t* loop) { int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop); return uv__kqueue_init(loop);
} }
@ -163,8 +170,15 @@ int uv_set_process_title(const char* title) {
char* new_title; char* new_title;
new_title = uv__strdup(title); new_title = uv__strdup(title);
if (process_title == NULL)
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title == NULL) {
uv_mutex_unlock(&process_title_mutex);
return -ENOMEM; return -ENOMEM;
}
uv__free(process_title); uv__free(process_title);
process_title = new_title; process_title = new_title;
@ -180,6 +194,8 @@ int uv_set_process_title(const char* title) {
process_title, process_title,
strlen(process_title) + 1); strlen(process_title) + 1);
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }
@ -190,17 +206,24 @@ int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0) if (buffer == NULL || size == 0)
return -EINVAL; return -EINVAL;
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title) { if (process_title) {
len = strlen(process_title) + 1; len = strlen(process_title) + 1;
if (size < len) if (size < len) {
uv_mutex_unlock(&process_title_mutex);
return -ENOBUFS; return -ENOBUFS;
}
memcpy(buffer, process_title, len); memcpy(buffer, process_title, len);
} else { } else {
len = 0; len = 0;
} }
uv_mutex_unlock(&process_title_mutex);
buffer[len] = '\0'; buffer[len] = '\0';
return 0; return 0;

View File

@ -40,9 +40,16 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
static uv_mutex_t process_title_mutex;
static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
static char *process_title; static char *process_title;
static void init_process_title_mutex_once(void) {
uv_mutex_init(&process_title_mutex);
}
int uv__platform_loop_init(uv_loop_t* loop) { int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop); return uv__kqueue_init(loop);
} }
@ -137,12 +144,21 @@ int uv_set_process_title(const char* title) {
char* new_title; char* new_title;
new_title = uv__strdup(title); new_title = uv__strdup(title);
if (process_title == NULL)
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title == NULL) {
uv_mutex_unlock(&process_title_mutex);
return -ENOMEM; return -ENOMEM;
}
uv__free(process_title); uv__free(process_title);
process_title = new_title; process_title = new_title;
setproctitle("%s", title); setproctitle("%s", title);
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }
@ -153,17 +169,24 @@ int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0) if (buffer == NULL || size == 0)
return -EINVAL; return -EINVAL;
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title) { if (process_title) {
len = strlen(process_title) + 1; len = strlen(process_title) + 1;
if (size < len) if (size < len) {
uv_mutex_unlock(&process_title_mutex);
return -ENOBUFS; return -ENOBUFS;
}
memcpy(buffer, process_title, len); memcpy(buffer, process_title, len);
} else { } else {
len = 0; len = 0;
} }
uv_mutex_unlock(&process_title_mutex);
buffer[len] = '\0'; buffer[len] = '\0';
return 0; return 0;

View File

@ -36,9 +36,16 @@
#include <unistd.h> #include <unistd.h>
static uv_mutex_t process_title_mutex;
static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
static char *process_title; static char *process_title;
static void init_process_title_mutex_once(void) {
uv_mutex_init(&process_title_mutex);
}
int uv__platform_loop_init(uv_loop_t* loop) { int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop); return uv__kqueue_init(loop);
} }
@ -149,11 +156,21 @@ int uv_set_process_title(const char* title) {
char* new_title; char* new_title;
new_title = uv__strdup(title); new_title = uv__strdup(title);
if (process_title == NULL)
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title == NULL) {
uv_mutex_unlock(&process_title_mutex);
return -ENOMEM; return -ENOMEM;
}
uv__free(process_title); uv__free(process_title);
process_title = new_title; process_title = new_title;
setproctitle("%s", title); setproctitle("%s", title);
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }
@ -164,17 +181,24 @@ int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0) if (buffer == NULL || size == 0)
return -EINVAL; return -EINVAL;
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (process_title) { if (process_title) {
len = strlen(process_title) + 1; len = strlen(process_title) + 1;
if (size < len) if (size < len) {
uv_mutex_unlock(&process_title_mutex);
return -ENOBUFS; return -ENOBUFS;
}
memcpy(buffer, process_title, len); memcpy(buffer, process_title, len);
} else { } else {
len = 0; len = 0;
} }
uv_mutex_unlock(&process_title_mutex);
buffer[len] = '\0'; buffer[len] = '\0';
return 0; return 0;

View File

@ -26,6 +26,8 @@
extern void uv__set_process_title(const char* title); extern void uv__set_process_title(const char* title);
static uv_mutex_t process_title_mutex;
static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
static void* args_mem; static void* args_mem;
static struct { static struct {
@ -34,6 +36,11 @@ static struct {
} process_title; } process_title;
static void init_process_title_mutex_once(void) {
uv_mutex_init(&process_title_mutex);
}
char** uv_setup_args(int argc, char** argv) { char** uv_setup_args(int argc, char** argv) {
char** new_argv; char** new_argv;
size_t size; size_t size;
@ -81,12 +88,16 @@ char** uv_setup_args(int argc, char** argv) {
int uv_set_process_title(const char* title) { int uv_set_process_title(const char* title) {
if (process_title.len == 0) uv_once(&process_title_mutex_once, init_process_title_mutex_once);
return 0; uv_mutex_lock(&process_title_mutex);
/* No need to terminate, byte after is always '\0'. */ if (process_title.len != 0) {
strncpy(process_title.str, title, process_title.len); /* No need to terminate, byte after is always '\0'. */
uv__set_process_title(title); strncpy(process_title.str, title, process_title.len);
uv__set_process_title(title);
}
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }
@ -95,14 +106,22 @@ int uv_set_process_title(const char* title) {
int uv_get_process_title(char* buffer, size_t size) { int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0) if (buffer == NULL || size == 0)
return -EINVAL; return -EINVAL;
else if (size <= process_title.len)
uv_once(&process_title_mutex_once, init_process_title_mutex_once);
uv_mutex_lock(&process_title_mutex);
if (size <= process_title.len) {
uv_mutex_unlock(&process_title_mutex);
return -ENOBUFS; return -ENOBUFS;
}
if (process_title.len != 0) if (process_title.len != 0)
memcpy(buffer, process_title.str, process_title.len + 1); memcpy(buffer, process_title.str, process_title.len + 1);
buffer[process_title.len] = '\0'; buffer[process_title.len] = '\0';
uv_mutex_unlock(&process_title_mutex);
return 0; return 0;
} }

View File

@ -214,6 +214,7 @@ TEST_DECLARE (async_null_cb)
TEST_DECLARE (eintr_handling) TEST_DECLARE (eintr_handling)
TEST_DECLARE (get_currentexe) TEST_DECLARE (get_currentexe)
TEST_DECLARE (process_title) TEST_DECLARE (process_title)
TEST_DECLARE (process_title_threadsafe)
TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (cwd_and_chdir)
TEST_DECLARE (get_memory) TEST_DECLARE (get_memory)
TEST_DECLARE (get_passwd) TEST_DECLARE (get_passwd)
@ -672,6 +673,7 @@ TASK_LIST_START
TEST_ENTRY (get_currentexe) TEST_ENTRY (get_currentexe)
TEST_ENTRY (process_title) TEST_ENTRY (process_title)
TEST_ENTRY (process_title_threadsafe)
TEST_ENTRY (cwd_and_chdir) TEST_ENTRY (cwd_and_chdir)

View File

@ -0,0 +1,90 @@
/* Copyright libuv project contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
#include <string.h>
#ifdef __APPLE__
# define NUM_ITERATIONS 20
#else
# define NUM_ITERATIONS 50
#endif
static const char* titles[] = {
"8L2NY0Kdj0XyNFZnmUZigIOfcWjyNr0SkMmUhKw99VLUsZFrvCQQC3XIRfNR8pjyMjXObllled",
"jUAcscJN49oLSN8GdmXj2Wo34XX2T2vp2j5khfajNQarlOulp57cE130yiY53ipJFnPyTn5i82",
"9niCI5icXGFS72XudhXqo5alftmZ1tpE7B3cwUmrq0CCDjC84FzBNB8XAHqvpNQfI2QAQG6ztT",
"n8qXVXuG6IEHDpabJgTEiwtpY6LHMZ8MgznnMpdHARu5EywufA6hcBaQfetb0YhEsK0ykDd7JU"
};
static void getter_thread_body(void* arg) {
char buffer[512];
for (;;) {
ASSERT(0 == uv_get_process_title(buffer, sizeof(buffer)));
ASSERT(
0 == strcmp(buffer, titles[0]) ||
0 == strcmp(buffer, titles[1]) ||
0 == strcmp(buffer, titles[2]) ||
0 == strcmp(buffer, titles[3]));
uv_sleep(0);
}
}
static void setter_thread_body(void* arg) {
int i;
for (i = 0; i < NUM_ITERATIONS; i++) {
ASSERT(0 == uv_set_process_title(titles[0]));
ASSERT(0 == uv_set_process_title(titles[1]));
ASSERT(0 == uv_set_process_title(titles[2]));
ASSERT(0 == uv_set_process_title(titles[3]));
}
}
TEST_IMPL(process_title_threadsafe) {
uv_thread_t setter_threads[4];
uv_thread_t getter_thread;
int i;
#if defined(__sun) || defined(__CYGWIN__) || defined(__MSYS__) || \
defined(__MVS__)
RETURN_SKIP("uv_(get|set)_process_title is not implemented.");
#else
ASSERT(0 == uv_set_process_title(titles[0]));
ASSERT(0 == uv_thread_create(&getter_thread, getter_thread_body, NULL));
for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++)
ASSERT(0 == uv_thread_create(&setter_threads[i], setter_thread_body, NULL));
for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++)
ASSERT(0 == uv_thread_join(&setter_threads[i]));
return 0;
#endif
}

1
uv.gyp
View File

@ -427,6 +427,7 @@
'test/test-poll-closesocket.c', 'test/test-poll-closesocket.c',
'test/test-poll-oob.c', 'test/test-poll-oob.c',
'test/test-process-title.c', 'test/test-process-title.c',
'test/test-process-title-threadsafe.c',
'test/test-queue-foreach-delete.c', 'test/test-queue-foreach-delete.c',
'test/test-ref.c', 'test/test-ref.c',
'test/test-run-nowait.c', 'test/test-run-nowait.c',