/*
* Copyright (c) 2006 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup libc
* @{
*/
/** @file
*/
#include <libc.h>
#include <stdbool.h>
#include <stdlib.h>
#include <libarch/faddr.h>
#include <fibril.h>
#include <stack.h>
#include <str.h>
#include <async.h>
#include <errno.h>
#include <as.h>
#include "../private/thread.h"
#include "../private/fibril.h"
/** Main thread function.
*
* This function is called from __thread_entry() and is used
* to call the thread's implementing function and perform cleanup
* and exit when thread returns back.
*
* @param arg Fibril pointer.
*
*/
static void __thread_main(void *arg)
{
fibril_t *fibril = arg;
assert(!__tcb_is_set());
assert(fibril);
__tcb_set(fibril->tcb);
fibril->func(fibril->arg);
/*
* XXX: we cannot free the userspace stack while running on it
*
* free(uarg->uspace_stack);
* free(uarg);
*/
fibril_teardown(fibril);
thread_exit(0);
}
/** Create userspace thread.
*
* This function creates new userspace thread and allocates userspace
* stack and userspace argument structure for it.
*
* @param function Function implementing the thread.
* @param arg Argument to be passed to thread.
* @param name Symbolic name of the thread.
* @param tid Thread ID of the newly created thread.
*
* @return Zero on success or a code from @ref errno.h on failure.
*/
errno_t thread_create(errno_t (*func)(void *), void *arg, const char *name)
{
fibril_t *fibril = fibril_alloc();
if (!fibril)
return ENOMEM;
fibril->func = func;
fibril->arg = arg;
size_t stack_size = stack_size_get();
void *stack = as_area_create(AS_AREA_ANY, stack_size,
AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
if (stack == AS_MAP_FAILED) {
fibril_teardown(fibril);
return ENOMEM;
}
uintptr_t sp = arch_thread_prepare(stack, stack_size, __thread_main,
fibril);
errno_t rc = (errno_t) __SYSCALL4(SYS_THREAD_CREATE,
(sysarg_t) FADDR(__thread_entry), sp,
(sysarg_t) name, (sysarg_t) str_size(name));
if (rc != EOK) {
/*
* Failed to create a new thread.
* Free up the allocated data.
*/
as_area_destroy(stack);
}
return rc;
}
/** Terminate current thread.
*
* @param status Exit status. Currently not used.
*
*/
void thread_exit(int status)
{
__SYSCALL1(SYS_THREAD_EXIT, (sysarg_t) status);
/* Unreachable */
while (true)
;
}
/** Get current thread ID.
*
* @return Current thread ID.
*/
thread_id_t thread_get_id(void)
{
thread_id_t thread_id;
(void) __SYSCALL1(SYS_THREAD_GET_ID, (sysarg_t) &thread_id);
return thread_id;
}
/** Wait unconditionally for specified number of microseconds
*
*/
void thread_usleep(usec_t usec)
{
(void) __SYSCALL1(SYS_THREAD_USLEEP, usec);
}
/** Wait unconditionally for specified number of seconds
*
*/
void thread_sleep(sec_t sec)
{
/*
* Sleep in 1000 second steps to support full argument range
*/
while (sec > 0) {
unsigned int period = (sec > 1000) ? 1000 : sec;
thread_usleep(SEC2USEC(period));
sec -= period;
}
}
/** @}
*/