src/lib/librthread/rthread_attr.c

242 lines
5.5 KiB
C

/* $OpenBSD: rthread_attr.c,v 1.25 2018/05/02 14:06:00 bluhm Exp $ */
/*
* Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
* All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* generic attribute support
*/
#include <sys/mman.h>
#include <stdint.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <pthread_np.h>
#include "rthread.h"
/*
* Note: stack_size + guard_size == total stack used
*
* pthread_attr_init MUST be called before any other attribute function
* for proper operation.
*
* Every call to pthread_attr_init MUST be matched with a call to
* pthread_attr_destroy to avoid leaking memory. This is an implementation
* requirement, not a POSIX requirement.
*/
int
pthread_attr_init(pthread_attr_t *attrp)
{
pthread_attr_t attr;
/* make sure _rthread_attr_default has been initialized */
if (!_threads_ready)
_rthread_init();
attr = calloc(1, sizeof(*attr));
if (!attr)
return (errno);
*attr = _rthread_attr_default;
*attrp = attr;
return (0);
}
int
pthread_attr_destroy(pthread_attr_t *attrp)
{
free(*attrp);
*attrp = NULL;
return (0);
}
int
pthread_attr_getguardsize(const pthread_attr_t *attrp, size_t *guardsize)
{
*guardsize = (*attrp)->guard_size;
return (0);
}
int
pthread_attr_setguardsize(pthread_attr_t *attrp, size_t guardsize)
{
(*attrp)->guard_size = guardsize;
return (0);
}
int
pthread_attr_getdetachstate(const pthread_attr_t *attrp, int *detachstate)
{
*detachstate = (*attrp)->detach_state;
return (0);
}
int
pthread_attr_setdetachstate(pthread_attr_t *attrp, int detachstate)
{
int error;
error = (detachstate == PTHREAD_CREATE_DETACHED ||
detachstate == PTHREAD_CREATE_JOINABLE) ? 0 : EINVAL;
if (error == 0)
(*attrp)->detach_state = detachstate;
return (error);
}
int
pthread_attr_getstack(const pthread_attr_t *attrp, void **stackaddr,
size_t *stacksize)
{
*stackaddr = (*attrp)->stack_addr;
*stacksize = (*attrp)->stack_size;
return (0);
}
int
pthread_attr_setstack(pthread_attr_t *attrp, void *stackaddr, size_t stacksize)
{
int error;
volatile char *p = stackaddr;
size_t i;
struct syslog_data data = SYSLOG_DATA_INIT;
if (stacksize < PTHREAD_STACK_MIN) {
syslog_r(LOG_ERR, &data,
"pthread_attr_setstack(%p, %zu): "
"stack size below min size %d",
stackaddr, stacksize, PTHREAD_STACK_MIN);
return (EINVAL);
}
/*
* Make sure that the stack is page-aligned and a multiple
* of the page size
*/
if (((uintptr_t)stackaddr % PTHREAD_STACK_MIN) != 0
|| (stacksize % PTHREAD_STACK_MIN) != 0) {
syslog_r(LOG_ERR, &data,
"pthread_attr_setstack(%p, 0x%zx): "
"unaligned thread stack start and/or size",
stackaddr, stacksize);
return (EINVAL);
}
/*
* We are going to re-mmap() stackaddr to MAP_STACK, but only
* if the entire range [stackaddr, stackaddr+stacksize) consists
* of valid address that are mapped PROT_READ|PROT_WRITE.
* Test this by reading and writing every page.
*
* XXX: What if the caller has SIGSEGV blocked or ignored?
* Then we won't crash here when entering an invalid mapping.
*/
for (i = 0; i < stacksize; i += PTHREAD_STACK_MIN) {
char val = p[i];
p[i] = val;
}
if (mmap(stackaddr, stacksize, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_STACK|MAP_ANON|MAP_PRIVATE, -1, 0) == MAP_FAILED) {
syslog_r(LOG_ERR, &data,
"pthread_attr_setstack(%p, %zu): mmap error %m",
stackaddr, stacksize);
return (errno);
}
if ((error = pthread_attr_setstackaddr(attrp, stackaddr)))
return (error);
(*attrp)->stack_size = stacksize;
return (0);
}
int
pthread_attr_getstacksize(const pthread_attr_t *attrp, size_t *stacksize)
{
*stacksize = (*attrp)->stack_size;
return (0);
}
int
pthread_attr_setstacksize(pthread_attr_t *attrp, size_t stacksize)
{
if (!_threads_ready) /* for ROUND_TO_PAGE */
_rthread_init();
if (stacksize < PTHREAD_STACK_MIN ||
stacksize > ROUND_TO_PAGE(stacksize))
return (EINVAL);
(*attrp)->stack_size = stacksize;
return (0);
}
int
pthread_attr_getstackaddr(const pthread_attr_t *attrp, void **stackaddr)
{
*stackaddr = (*attrp)->stack_addr;
return (0);
}
int
pthread_attr_setstackaddr(pthread_attr_t *attrp, void *stackaddr)
{
if (!_threads_ready)
_rthread_init(); /* for _thread_pagesize */
if (stackaddr == NULL || (uintptr_t)stackaddr & (_thread_pagesize - 1))
return (EINVAL);
(*attrp)->stack_addr = stackaddr;
return (0);
}
DEF_NONSTD(pthread_attr_setstackaddr);
int
pthread_attr_getscope(const pthread_attr_t *attrp, int *contentionscope)
{
*contentionscope = (*attrp)->contention_scope;
return (0);
}
int
pthread_attr_setscope(pthread_attr_t *attrp, int contentionscope)
{
if (contentionscope != PTHREAD_SCOPE_SYSTEM &&
contentionscope != PTHREAD_SCOPE_PROCESS)
return (EINVAL);
(*attrp)->contention_scope = contentionscope;
return (0);
}