/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program 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 Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#ifdef HAVE_PTHREAD_NP_H
# include <pthread_np.h>
#endif
#ifdef OF_HAIKU
# include <kernel/OS.h>
#endif
#import "OFPlainThread.h"
#import "macros.h"
static int minPrio = 0, maxPrio = 0, normalPrio = 0;
struct ThreadContext {
void (*function)(id object);
id object;
const char *name;
};
/*
* This is done here to make sure this is done as early as possible in the main
* thread.
*/
OF_CONSTRUCTOR()
{
pthread_attr_t attr;
if (pthread_attr_init(&attr) == 0) {
#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
int policy;
#endif
struct sched_param param;
#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
if (pthread_attr_getschedpolicy(&attr, &policy) == 0) {
minPrio = sched_get_priority_min(policy);
maxPrio = sched_get_priority_max(policy);
if (minPrio == -1 || maxPrio == -1)
minPrio = maxPrio = 0;
}
#endif
if (pthread_attr_getschedparam(&attr, ¶m) != 0)
normalPrio = param.sched_priority;
else
minPrio = maxPrio = 0;
pthread_attr_destroy(&attr);
}
}
static void *
functionWrapper(void *data)
{
struct ThreadContext *ctx = data;
if (ctx->name != NULL)
OFSetThreadName(ctx->name);
pthread_cleanup_push(free, data);
ctx->function(ctx->object);
pthread_cleanup_pop(1);
return NULL;
}
int
OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr)
{
int error;
pthread_attr_t POSIXAttr;
attr->priority = 0;
attr->stackSize = 0;
if ((error = pthread_attr_init(&POSIXAttr)) != 0) {
if (error == ENOSYS)
return 0;
return error;
}
error = pthread_attr_getstacksize(&POSIXAttr, &attr->stackSize);
pthread_attr_destroy(&POSIXAttr);
return error;
}
int
OFPlainThreadNew(OFPlainThread *thread, const char *name, void (*function)(id),
id object, const OFPlainThreadAttributes *attr)
{
int error = 0;
pthread_attr_t POSIXAttr;
bool POSIXAttrAvailable = true;
if ((error = pthread_attr_init(&POSIXAttr)) != 0) {
if (error == ENOSYS)
POSIXAttrAvailable = false;
else
return error;
}
@try {
struct ThreadContext *ctx;
if (attr != NULL && POSIXAttrAvailable) {
#ifndef OF_HPUX
struct sched_param param;
#endif
if (attr->priority < -1 || attr->priority > 1)
return EINVAL;
#ifndef OF_HPUX
# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
if ((error = pthread_attr_setinheritsched(&POSIXAttr,
PTHREAD_EXPLICIT_SCHED)) != 0)
return error;
# endif
if ((error = pthread_attr_getschedparam(&POSIXAttr,
¶m)) != 0)
return error;
if (attr->priority < 0) {
param.sched_priority = minPrio +
(1.0f + attr->priority) *
(normalPrio - minPrio);
} else
param.sched_priority = normalPrio +
attr->priority * (maxPrio - normalPrio);
if ((error = pthread_attr_setschedparam(&POSIXAttr,
¶m)) != 0)
return error;
#endif
if (attr->stackSize > 0) {
if ((error = pthread_attr_setstacksize(
&POSIXAttr, attr->stackSize)) != 0)
return error;
}
}
if ((ctx = malloc(sizeof(*ctx))) == NULL)
return ENOMEM;
ctx->function = function;
ctx->object = object;
ctx->name = name;
error = pthread_create(thread,
(POSIXAttrAvailable ? &POSIXAttr : NULL), functionWrapper,
ctx);
} @finally {
if (POSIXAttrAvailable)
pthread_attr_destroy(&POSIXAttr);
}
return error;
}
int
OFPlainThreadJoin(OFPlainThread thread)
{
void *ret;
return pthread_join(thread, &ret);
}
int
OFPlainThreadDetach(OFPlainThread thread)
{
return pthread_detach(thread);
}
void
OFSetThreadName(const char *name)
{
#if defined(OF_HAIKU)
rename_thread(find_thread(NULL), name);
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
pthread_set_name_np(pthread_self(), name);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
# if defined(OF_MACOS) || defined(OF_IOS)
pthread_setname_np(name);
# elif defined(__GLIBC__)
char buffer[16];
strncpy(buffer, name, 15);
buffer[15] = 0;
pthread_setname_np(pthread_self(), buffer);
# endif
#endif
}