blob: 715c046cd7137dbbc6e8905f6eaae2f6378b3f4c [file] [log] [blame] [edit]
/*****************************************************************************\
* threadpool.c - Thread Pool
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "config.h"
#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#include <pthread.h>
#include <stdint.h>
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/read_config.h"
#include "src/common/slurm_time.h"
#include "src/common/threadpool.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
/*
* From man prctl:
* If the length of the string, including the terminating null byte,
* exceeds 16 bytes, the string is silently truncated.
*/
#define PRCTL_BUF_BYTES 17
/* default thread name for logging */
#define DEFAULT_THREAD_NAME "thread"
#define CTIME_STR_LEN 72
#ifdef PTHREAD_SCOPE_SYSTEM
#define slurm_attr_init(attr) \
do { \
int err = pthread_attr_init(attr); \
if (err) { \
errno = err; \
fatal("pthread_attr_init: %m"); \
} \
/* we want 1:1 threads if there is a choice */ \
err = pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM); \
if (err) { \
errno = err; \
error("pthread_attr_setscope: %m"); \
} \
err = pthread_attr_setstacksize(attr, STACK_SIZE); \
if (err) { \
errno = err; \
error("pthread_attr_setstacksize: %m"); \
} \
} while (0)
#else
#define slurm_attr_init(attr) \
do { \
int err = pthread_attr_init(attr); \
if (err) { \
errno = err; \
fatal("pthread_attr_init: %m"); \
} \
err = pthread_attr_setstacksize(attr, STACK_SIZE); \
if (err) { \
errno = err; \
error("pthread_attr_setstacksize: %m"); \
} \
} while (0)
#endif
#define THREAD_MAGIC 0xA434F4D2
typedef struct {
int magic; /* THREAD_MAGIC */
bool detached;
threadpool_func_t func;
timespec_t requested;
const char *thread_name;
const char *func_name;
void *arg;
} thread_t;
/* Define slurm-specific aliases for use by plugins, see slurm_xlator.h. */
strong_alias(threadpool_create, slurm_threadpool_create);
strong_alias(threadpool_join, slurm_threadpool_join);
extern int threadpool_join(const pthread_t id, const char *caller)
{
int rc = EINVAL;
void *ret = 0;
if (!id) {
log_flag(THREAD, "%s->%s: Ignoring invalid pthread id=0x0",
caller, __func__);
return SLURM_SUCCESS;
}
if ((rc = pthread_join(id, &ret))) {
error("%s->%s: pthread_join(id=0x%"PRIx64") failed: %m",
caller, __func__, (uint64_t) id);
return rc;
}
if (ret == PTHREAD_CANCELED)
log_flag(THREAD, "%s->%s: pthread id=0x%"PRIx64" was cancelled",
caller, __func__, (uint64_t) id);
else
log_flag(THREAD, "%s->%s: pthread id=0x%"PRIx64" returned: 0x%"PRIxPTR,
caller, __func__, (uint64_t) id, (uintptr_t) ret);
return rc;
}
static void _set_thread_name(const char *name)
{
#if HAVE_SYS_PRCTL_H
if (prctl(PR_SET_NAME, name, NULL, NULL, NULL))
error("%s: cannot set process name to %s %m", __func__, name);
#endif
}
static void _thread_free(thread_t *thread)
{
xassert(thread);
xassert(thread->magic == THREAD_MAGIC);
thread->magic = ~THREAD_MAGIC;
xfree(thread->thread_name);
xfree(thread);
}
static void *_thread(void *arg)
{
thread_t *thread = arg;
void *ret = NULL;
timespec_t start = { 0 }, end = { 0 };
xassert(thread->magic == THREAD_MAGIC);
if (thread->thread_name)
_set_thread_name(thread->thread_name);
if (slurm_conf.debug_flags & DEBUG_FLAG_THREAD) {
char ts[CTIME_STR_LEN] = "UNKNOWN";
start = timespec_now();
if (thread->requested.tv_sec) {
timespec_diff_ns_t diff =
timespec_diff_ns(start, thread->requested);
timespec_ctime(diff.diff, false, ts, sizeof(ts));
}
log_flag(THREAD, "%s: [%s@0x%"PRIx64"] BEGIN: %s thread calling %s(0x%"PRIxPTR") after %s",
__func__,
(thread->thread_name ? thread->thread_name : DEFAULT_THREAD_NAME),
(uint64_t) pthread_self(),
(thread->detached ? "detached" : "attached"),
thread->func_name, (uintptr_t) thread->arg, ts);
}
ret = thread->func(thread->arg);
if (slurm_conf.debug_flags & DEBUG_FLAG_THREAD) {
char ts[CTIME_STR_LEN] = "UNKNOWN";
end = timespec_now();
if (start.tv_sec) {
timespec_diff_ns_t diff = timespec_diff_ns(end, start);
timespec_ctime(diff.diff, false, ts, sizeof(ts));
}
log_flag(THREAD, "%s: [%s@0x%"PRIx64"] END: %s thread called %s(0x%"PRIxPTR")=0x%"PRIxPTR" for %s",
__func__,
(thread->thread_name ? thread->thread_name : DEFAULT_THREAD_NAME),
(uint64_t) pthread_self(),
(thread->detached ? "detached" : "attached"),
thread->func_name,
(uintptr_t) thread->arg, (uintptr_t) ret, ts);
}
_thread_free(thread);
return ret;
}
static void _free_attr(pthread_attr_t *attr)
{
int rc = EINVAL;
if ((rc = pthread_attr_destroy(attr)))
fatal("%s: pthread_attr_destroy failed: %s",
__func__, slurm_strerror(rc));
}
extern int threadpool_create(threadpool_func_t func, const char *func_name,
void *arg, const bool detached,
const char *thread_name, pthread_t *id_ptr,
const char *caller)
{
pthread_t id = 0;
pthread_attr_t attr;
int rc = EINVAL;
thread_t *thread = xmalloc(sizeof(*thread));
#ifndef NDEBUG
if (thread_name && strlen(thread_name) >= PRCTL_BUF_BYTES)
warning("%s: Thread name truncated[%zu/%zu]: %s",
caller, (uint64_t) strlen(thread_name),
(uint64_t) PRCTL_BUF_BYTES, thread_name);
#endif
*thread = (thread_t) {
.magic = THREAD_MAGIC,
.detached = detached,
.thread_name = xstrdup(thread_name),
.func = func,
.func_name = func_name,
.arg = arg,
};
if (slurm_conf.debug_flags & DEBUG_FLAG_THREAD)
thread->requested = timespec_now();
if ((rc = pthread_attr_init(&attr)))
fatal("%s->%s: pthread_attr_init() failed: %s",
caller, __func__, slurm_strerror(rc));
#ifdef PTHREAD_SCOPE_SYSTEM
/* we want 1:1 threads if there is a choice */
if ((rc = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)))
fatal("%s->%s: pthread_attr_setscope(PTHREAD_SCOPE_SYSTEM) failed: %s",
caller, __func__, slurm_strerror(rc));
#endif
if ((rc = pthread_attr_setstacksize(&attr, STACK_SIZE)))
fatal("%s->%s: pthread_attr_setstacksize(%u) failed: %s",
caller, __func__, STACK_SIZE, slurm_strerror(rc));
if (id_ptr)
*id_ptr = 0;
if (detached &&
(rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)))
fatal("%s->%s: pthread_attr_setdetachstate failed: %s",
caller, __func__, slurm_strerror(rc));
/* Pass ownership of thread to _thread() on success */
rc = pthread_create(&id, &attr, _thread, thread);
if (rc) {
error("%s->%s: pthread_create() failed: %s",
caller, __func__, slurm_strerror(rc));
_thread_free(thread);
} else {
log_flag(THREAD, "%s->%s: pthread_create() created new %s pthread id=0x%"PRIx64" for %s()",
caller, __func__, (detached ? "detached" : "attached"),
(uint64_t) id, func_name);
}
_free_attr(&attr);
if (id_ptr)
*id_ptr = id;
return rc;
}