/trunk/uspace/libc/generic/string.c |
---|
0,0 → 1,333 |
/* |
* Copyright (c) 2005 Martin Decky |
* 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 <string.h> |
#include <unistd.h> |
#include <ctype.h> |
#include <limits.h> |
#include <align.h> |
/* Dummy implementation of mem/ functions */ |
void *memset(void *s, int c, size_t n) |
{ |
char *os = s; |
while (n--) |
*(os++) = c; |
return s; |
} |
struct along { |
unsigned long n; |
} __attribute__ ((packed)); |
static void *unaligned_memcpy(void *dst, const void *src, size_t n) |
{ |
int i, j; |
struct along *adst = dst; |
const struct along *asrc = src; |
for (i = 0; i < n / sizeof(unsigned long); i++) |
adst[i].n = asrc[i].n; |
for (j = 0; j < n % sizeof(unsigned long); j++) |
((unsigned char *) (((unsigned long *) dst) + i))[j] = ((unsigned char *) (((unsigned long *) src) + i))[j]; |
return (char *) src; |
} |
void *memcpy(void *dst, const void *src, size_t n) |
{ |
int i, j; |
if (((long) dst & (sizeof(long) - 1)) || ((long) src & (sizeof(long) - 1))) |
return unaligned_memcpy(dst, src, n); |
for (i = 0; i < n / sizeof(unsigned long); i++) |
((unsigned long *) dst)[i] = ((unsigned long *) src)[i]; |
for (j = 0; j < n % sizeof(unsigned long); j++) |
((unsigned char *) (((unsigned long *) dst) + i))[j] = ((unsigned char *) (((unsigned long *) src) + i))[j]; |
return (char *) src; |
} |
void *memmove(void *dst, const void *src, size_t n) |
{ |
int i, j; |
if (src > dst) |
return memcpy(dst, src, n); |
for (j = (n % sizeof(unsigned long)) - 1; j >= 0; j--) |
((unsigned char *) ((unsigned long *) dst))[j] = ((unsigned char *) ((unsigned long *) src))[j]; |
for (i = n / sizeof(unsigned long) - 1; i >=0 ; i--) |
((unsigned long *) dst)[i] = ((unsigned long *) src)[i]; |
return (char *) src; |
} |
/** Compare two memory areas. |
* |
* @param s1 Pointer to the first area to compare. |
* @param s2 Pointer to the second area to compare. |
* @param len Size of the first area in bytes. Both areas must have the same |
* length. |
* @return If len is 0, return zero. If the areas match, return zero. |
* Otherwise return non-zero. |
*/ |
int bcmp(const char *s1, const char *s2, size_t len) |
{ |
for (; len && *s1++ == *s2++; len--) |
; |
return len; |
} |
/** Count the number of characters in the string, not including terminating 0. |
* @param str string |
* @return number of characters in string. |
*/ |
size_t strlen(const char *str) |
{ |
size_t counter = 0; |
while (str[counter] != 0) |
counter++; |
return counter; |
} |
int strcmp(const char *a, const char *b) |
{ |
int c = 0; |
while (a[c] && b[c] && (!(a[c] - b[c]))) |
c++; |
return (a[c] - b[c]); |
} |
/** Return pointer to the first occurence of character c in string |
* @param str scanned string |
* @param c searched character (taken as one byte) |
* @return pointer to the matched character or NULL if it is not found in given string. |
*/ |
char *strchr(const char *str, int c) |
{ |
while (*str != '\0') { |
if (*str == (char) c) |
return (char *) str; |
str++; |
} |
return NULL; |
} |
/** Return pointer to the last occurence of character c in string |
* @param str scanned string |
* @param c searched character (taken as one byte) |
* @return pointer to the matched character or NULL if it is not found in given string. |
*/ |
char *strrchr(const char *str, int c) |
{ |
char *retval = NULL; |
while (*str != '\0') { |
if (*str == (char) c) |
retval = (char *) str; |
str++; |
} |
return (char *) retval; |
} |
/** Convert string to a number. |
* Core of strtol and strtoul functions. |
* @param nptr pointer to string |
* @param endptr if not NULL, function stores here pointer to the first invalid character |
* @param base zero or number between 2 and 36 inclusive |
* @param sgn its set to 1 if minus found |
* @return result of conversion. |
*/ |
static unsigned long _strtoul(const char *nptr, char **endptr, int base, char *sgn) |
{ |
unsigned char c; |
unsigned long result = 0; |
unsigned long a, b; |
const char *str = nptr; |
const char *tmpptr; |
while (isspace(*str)) |
str++; |
if (*str == '-') { |
*sgn = 1; |
++str; |
} else if (*str == '+') |
++str; |
if (base) { |
if ((base == 1) || (base > 36)) { |
/* FIXME: set errno to EINVAL */ |
return 0; |
} |
if ((base == 16) && (*str == '0') && ((str[1] == 'x') || (str[1] == 'X'))) { |
str += 2; |
} |
} else { |
base = 10; |
if (*str == '0') { |
base = 8; |
if ((str[1] == 'X') || (str[1] == 'x')) { |
base = 16; |
str += 2; |
} |
} |
} |
tmpptr = str; |
while (*str) { |
c = *str; |
c = (c >= 'a' ? c - 'a' + 10 : (c >= 'A' ? c - 'A' + 10 : (c <= '9' ? c - '0' : 0xff))); |
if (c > base) { |
break; |
} |
a = (result & 0xff) * base + c; |
b = (result >> 8) * base + (a >> 8); |
if (b > (ULONG_MAX >> 8)) { |
/* overflow */ |
/* FIXME: errno = ERANGE*/ |
return ULONG_MAX; |
} |
result = (b << 8) + (a & 0xff); |
++str; |
} |
if (str == tmpptr) { |
/* no number was found => first invalid character is the first character of the string */ |
/* FIXME: set errno to EINVAL */ |
str = nptr; |
result = 0; |
} |
if (endptr) |
*endptr = (char *) str; |
if (nptr == str) { |
/*FIXME: errno = EINVAL*/ |
return 0; |
} |
return result; |
} |
/** Convert initial part of string to long int according to given base. |
* The number may begin with an arbitrary number of whitespaces followed by optional sign (`+' or `-'). |
* If the base is 0 or 16, the prefix `0x' may be inserted and the number will be taken as hexadecimal one. |
* If the base is 0 and the number begin with a zero, number will be taken as octal one (as with base 8). |
* Otherwise the base 0 is taken as decimal. |
* @param nptr pointer to string |
* @param endptr if not NULL, function stores here pointer to the first invalid character |
* @param base zero or number between 2 and 36 inclusive |
* @return result of conversion. |
*/ |
long int strtol(const char *nptr, char **endptr, int base) |
{ |
char sgn = 0; |
unsigned long number = 0; |
number = _strtoul(nptr, endptr, base, &sgn); |
if (number > LONG_MAX) { |
if ((sgn) && (number == (unsigned long) (LONG_MAX) + 1)) { |
/* FIXME: set 0 to errno */ |
return number; |
} |
/* FIXME: set ERANGE to errno */ |
return (sgn ? LONG_MIN : LONG_MAX); |
} |
return (sgn ? -number : number); |
} |
/** Convert initial part of string to unsigned long according to given base. |
* The number may begin with an arbitrary number of whitespaces followed by optional sign (`+' or `-'). |
* If the base is 0 or 16, the prefix `0x' may be inserted and the number will be taken as hexadecimal one. |
* If the base is 0 and the number begin with a zero, number will be taken as octal one (as with base 8). |
* Otherwise the base 0 is taken as decimal. |
* @param nptr pointer to string |
* @param endptr if not NULL, function stores here pointer to the first invalid character |
* @param base zero or number between 2 and 36 inclusive |
* @return result of conversion. |
*/ |
unsigned long strtoul(const char *nptr, char **endptr, int base) |
{ |
char sgn = 0; |
unsigned long number = 0; |
number = _strtoul(nptr, endptr, base, &sgn); |
return (sgn ? -number : number); |
} |
char *strcpy(char *dest, const char *src) |
{ |
char *orig = dest; |
while ((*(dest++) = *(src++))); |
return orig; |
} |
char *strncpy(char *dest, const char *src, size_t n) |
{ |
char *orig = dest; |
while ((*(dest++) = *(src++)) && --n); |
return orig; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/time.c |
---|
0,0 → 1,122 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 <sys/time.h> |
#include <unistd.h> |
#include <ipc/ipc.h> |
#include <stdio.h> |
#include <arch/barrier.h> |
#include <unistd.h> |
#include <atomic.h> |
#include <futex.h> |
#include <sysinfo.h> |
#include <ipc/services.h> |
#include <sysinfo.h> |
#include <as.h> |
#include <ddi.h> |
/* Pointers to public variables with time */ |
struct { |
volatile sysarg_t seconds1; |
volatile sysarg_t useconds; |
volatile sysarg_t seconds2; |
} *ktime = NULL; |
/** POSIX gettimeofday |
* |
* The time variables are memory mapped(RO) from kernel, which updates |
* them periodically. As it is impossible to read 2 values atomically, we |
* use a trick: First read a seconds, then read microseconds, then |
* read seconds again. If a second elapsed in the meantime, set it to zero. |
* This provides assurance, that at least the |
* sequence of subsequent gettimeofday calls is ordered. |
*/ |
int gettimeofday(struct timeval *tv, struct timezone *tz) |
{ |
void *mapping; |
sysarg_t s1, s2; |
sysarg_t rights; |
int res; |
if (!ktime) { |
mapping = as_get_mappable_page(PAGE_SIZE, (int) |
sysinfo_value("clock.fcolor")); |
/* Get the mapping of kernel clock */ |
res = ipc_call_sync_3(PHONE_NS, IPC_M_AS_AREA_RECV, (sysarg_t) |
mapping, PAGE_SIZE, SERVICE_MEM_REALTIME, NULL, &rights, |
NULL); |
if (res) { |
printf("Failed to initialize timeofday memarea\n"); |
_exit(1); |
} |
if (! (rights & AS_AREA_READ)) { |
printf("Received bad rights on time area: %X\n", |
rights); |
as_area_destroy(mapping); |
_exit(1); |
} |
ktime = mapping; |
} |
if (tz) { |
tz->tz_minuteswest = 0; |
tz->tz_dsttime = DST_NONE; |
} |
s2 = ktime->seconds2; |
read_barrier(); |
tv->tv_usec = ktime->useconds; |
read_barrier(); |
s1 = ktime->seconds1; |
if (s1 != s2) { |
tv->tv_usec = 0; |
tv->tv_sec = s1 > s2 ? s1 : s2; |
} else |
tv->tv_sec = s1; |
return 0; |
} |
/** Wait unconditionally for specified microseconds */ |
void usleep(unsigned long usec) |
{ |
atomic_t futex = FUTEX_INITIALIZER; |
futex_initialize(&futex,0); |
futex_down_timeout(&futex, usec, 0); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/ddi.c |
---|
0,0 → 1,91 |
/* |
* 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 <ddi.h> |
#include <libc.h> |
#include <task.h> |
#include <kernel/ddi/ddi_arg.h> |
/** Map piece of physical memory to task. |
* |
* Caller of this function must have the CAP_MEM_MANAGER capability. |
* |
* @param pf Physical address of the starting frame. |
* @param vp Virtual address of the sterting page. |
* @param pages Number of pages to map. |
* @param flags Flags for the new address space area. |
* |
* @return 0 on success, EPERM if the caller lacks the CAP_MEM_MANAGER capability, |
* ENOENT if there is no task with specified ID and ENOMEM if there |
* was some problem in creating address space area. |
*/ |
int physmem_map(void *pf, void *vp, unsigned long pages, int flags) |
{ |
return __SYSCALL4(SYS_PHYSMEM_MAP, (sysarg_t) pf, (sysarg_t) vp, pages, flags); |
} |
/** Enable I/O space range to task. |
* |
* Caller of this function must have the IO_MEM_MANAGER capability. |
* |
* @param id Task ID. |
* @param ioaddr Starting address of the I/O range. |
* @param size Size of the range. |
* |
* @return 0 on success, EPERM if the caller lacks the CAP_IO_MANAGER capability, |
* ENOENT if there is no task with specified ID and ENOMEM if there |
* was some problem in allocating memory. |
*/ |
int iospace_enable(task_id_t id, void *ioaddr, unsigned long size) |
{ |
ddi_ioarg_t arg; |
arg.task_id = id; |
arg.ioaddr = ioaddr; |
arg.size = size; |
return __SYSCALL1(SYS_IOSPACE_ENABLE, (sysarg_t) &arg); |
} |
/** Interrupt control |
* |
* @param enable 1 - enable interrupts, 0 - disable interrupts |
*/ |
int preemption_control(int enable) |
{ |
return __SYSCALL1(SYS_PREEMPT_CONTROL, (sysarg_t) enable); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/task.c |
---|
0,0 → 1,48 |
/* |
* 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 <task.h> |
#include <libc.h> |
task_id_t task_get_id(void) |
{ |
task_id_t task_id; |
(void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id); |
return task_id; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/cap.c |
---|
0,0 → 1,75 |
/* |
* 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 cap.c |
* @brief Functions to grant/revoke capabilities to/from a task. |
*/ |
#include <cap.h> |
#include <task.h> |
#include <libc.h> |
#include <kernel/syscall/sysarg64.h> |
/** Grant capabilities to a task. |
* |
* @param id Destination task ID. |
* @param caps Capabilities to grant. |
* |
* @return Zero on success or a value from @ref errno.h on failure. |
*/ |
int cap_grant(task_id_t id, unsigned int caps) |
{ |
sysarg64_t arg; |
arg.value = (unsigned long long) id; |
return __SYSCALL2(SYS_CAP_GRANT, (sysarg_t) &arg, (sysarg_t) caps); |
} |
/** Revoke capabilities from a task. |
* |
* @param id Destination task ID. |
* @param caps Capabilities to revoke. |
* |
* @return Zero on success or a value from @ref errno.h on failure. |
*/ |
int cap_revoke(task_id_t id, unsigned int caps) |
{ |
sysarg64_t arg; |
arg.value = (unsigned long long) id; |
return __SYSCALL2(SYS_CAP_REVOKE, (sysarg_t) &arg, (sysarg_t) caps); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/as.c |
---|
0,0 → 1,193 |
/* |
* 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 <as.h> |
#include <libc.h> |
#include <unistd.h> |
#include <align.h> |
#include <types.h> |
#include <bitops.h> |
/** |
* Either 4*256M on 32-bit architecures or 16*256M on 64-bit architectures. |
*/ |
#define MAX_HEAP_SIZE (sizeof(uintptr_t)<<28) |
/** Create address space area. |
* |
* @param address Virtual address where to place new address space area. |
* @param size Size of the area. |
* @param flags Flags describing type of the area. |
* |
* @return address on success, (void *) -1 otherwise. |
*/ |
void *as_area_create(void *address, size_t size, int flags) |
{ |
return (void *) __SYSCALL3(SYS_AS_AREA_CREATE, (sysarg_t ) address, |
(sysarg_t) size, (sysarg_t) flags); |
} |
/** Resize address space area. |
* |
* @param address Virtual address pointing into already existing address space |
* area. |
* @param size New requested size of the area. |
* @param flags Currently unused. |
* |
* @return Zero on success or a code from @ref errno.h on failure. |
*/ |
int as_area_resize(void *address, size_t size, int flags) |
{ |
return __SYSCALL3(SYS_AS_AREA_RESIZE, (sysarg_t ) address, (sysarg_t) |
size, (sysarg_t) flags); |
} |
/** Destroy address space area. |
* |
* @param address Virtual address pointing into the address space area being |
* destroyed. |
* |
* @return Zero on success or a code from @ref errno.h on failure. |
*/ |
int as_area_destroy(void *address) |
{ |
return __SYSCALL1(SYS_AS_AREA_DESTROY, (sysarg_t ) address); |
} |
static size_t heapsize = 0; |
static size_t maxheapsize = (size_t) (-1); |
static void * last_allocated = 0; |
/* Start of heap linker symbol */ |
extern char _heap; |
/** Sbrk emulation |
* |
* @param incr New area that should be allocated or negative, |
if it should be shrinked |
* @return Pointer to newly allocated area |
*/ |
void *sbrk(ssize_t incr) |
{ |
int rc; |
void *res; |
/* Check for invalid values */ |
if (incr < 0 && -incr > heapsize) |
return NULL; |
/* Check for too large value */ |
if (incr > 0 && incr+heapsize < heapsize) |
return NULL; |
/* Check for too small values */ |
if (incr < 0 && incr+heapsize > heapsize) |
return NULL; |
/* Check for user limit */ |
if ((maxheapsize != (size_t) (-1)) && (heapsize + incr) > maxheapsize) |
return NULL; |
rc = as_area_resize(&_heap, heapsize + incr, 0); |
if (rc != 0) |
return NULL; |
/* Compute start of new area */ |
res = (void *) &_heap + heapsize; |
heapsize += incr; |
return res; |
} |
/** Set maximum heap size and return pointer just after the heap */ |
void *set_maxheapsize(size_t mhs) |
{ |
maxheapsize = mhs; |
/* Return pointer to area not managed by sbrk */ |
return ((void *) &_heap + maxheapsize); |
} |
/** Return pointer to some unmapped area, where fits new as_area |
* |
* @param sz Requested size of the allocation. |
* @param color Requested virtual color of the allocation. |
* |
* @return Pointer to the beginning |
* |
* TODO: make some first_fit/... algorithm, we are now just incrementing |
* the pointer to last area |
*/ |
#include <stdio.h> |
void *as_get_mappable_page(size_t sz, int color) |
{ |
void *res; |
uint64_t asz; |
int i; |
if (!sz) |
return NULL; |
asz = 1 << (fnzb64(sz - 1) + 1); |
/* Set heapsize to some meaningful value */ |
if (maxheapsize == -1) |
set_maxheapsize(MAX_HEAP_SIZE); |
/* |
* Make sure we allocate from naturally aligned address and a page of |
* appropriate color. |
*/ |
i = 0; |
do { |
if (!last_allocated) { |
last_allocated = (void *) ALIGN_UP((void *) &_heap + |
maxheapsize, asz); |
} else { |
last_allocated = (void *) ALIGN_UP(((uintptr_t) |
last_allocated) + (int) (i > 0), asz); |
} |
} while ((asz < (1 << (PAGE_COLOR_BITS + PAGE_WIDTH))) && |
(PAGE_COLOR((uintptr_t) last_allocated) != color) && |
(++i < (1 << PAGE_COLOR_BITS))); |
res = last_allocated; |
last_allocated += ALIGN_UP(sz, PAGE_SIZE); |
return res; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/thread.c |
---|
0,0 → 1,164 |
/* |
* 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 <thread.h> |
#include <libc.h> |
#include <stdlib.h> |
#include <libarch/faddr.h> |
#include <kernel/proc/uarg.h> |
#include <psthread.h> |
#include <string.h> |
#include <async.h> |
#include <stdio.h> |
#ifndef THREAD_INITIAL_STACK_PAGES_NO |
#define THREAD_INITIAL_STACK_PAGES_NO 1 |
#endif |
static LIST_INITIALIZE(thread_garbage); |
extern char _tdata_start; |
extern char _tdata_end; |
extern char _tbss_start; |
extern char _tbss_end; |
/** Create TLS (Thread Local Storage) data structures. |
* |
* The code requires, that sections .tdata and .tbss are adjacent. It may be |
* changed in the future. |
* |
* @return Pointer to TCB. |
*/ |
tcb_t *__make_tls(void) |
{ |
void *data; |
tcb_t *tcb; |
size_t tls_size = &_tbss_end - &_tdata_start; |
tcb = __alloc_tls(&data, tls_size); |
/* |
* Copy thread local data from the initialization image. |
*/ |
memcpy(data, &_tdata_start, &_tdata_end - &_tdata_start); |
/* |
* Zero out the thread local uninitialized data. |
*/ |
memset(data + (&_tbss_start - &_tdata_start), 0, &_tbss_end - |
&_tbss_start); |
return tcb; |
} |
void __free_tls(tcb_t *tcb) |
{ |
size_t tls_size = &_tbss_end - &_tdata_start; |
__free_tls_arch(tcb, tls_size); |
} |
/** 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. Do not call this function |
* directly. |
* |
* @param uarg Pointer to userspace argument structure. |
*/ |
void __thread_main(uspace_arg_t *uarg) |
{ |
psthread_data_t *pt; |
pt = psthread_setup(); |
__tcb_set(pt->tcb); |
uarg->uspace_thread_function(uarg->uspace_thread_arg); |
free(uarg->uspace_stack); |
free(uarg); |
/* If there is a manager, destroy it */ |
async_destroy_manager(); |
psthread_teardown(pt); |
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. |
* |
* @return TID of the new thread on success or -1 on failure. |
*/ |
int thread_create(void (* function)(void *), void *arg, char *name) |
{ |
char *stack; |
uspace_arg_t *uarg; |
stack = (char *) malloc(getpagesize() * THREAD_INITIAL_STACK_PAGES_NO); |
if (!stack) |
return -1; |
uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t)); |
if (!uarg) { |
free(stack); |
return -1; |
} |
uarg->uspace_entry = (void *) FADDR(__thread_entry); |
uarg->uspace_stack = (void *) stack; |
uarg->uspace_thread_function = function; |
uarg->uspace_thread_arg = arg; |
uarg->uspace_uarg = uarg; |
return __SYSCALL2(SYS_THREAD_CREATE, (sysarg_t) uarg, (sysarg_t) name); |
} |
/** Terminate current thread. |
* |
* @param status Exit status. Currently not used. |
*/ |
void thread_exit(int status) |
{ |
__SYSCALL1(SYS_THREAD_EXIT, (sysarg_t) status); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/libc.c |
---|
0,0 → 1,82 |
/* |
* Copyright (c) 2005 Martin Decky |
* 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 lc Libc |
* @brief HelenOS C library |
* @{ |
* @} |
*/ |
/** @addtogroup libc generic |
* @ingroup lc |
* @{ |
*/ |
/** @file |
*/ |
#include <libc.h> |
#include <unistd.h> |
#include <thread.h> |
#include <malloc.h> |
#include <psthread.h> |
#include <io/stream.h> |
#include <ipc/ipc.h> |
#include <async.h> |
#include <as.h> |
extern char _heap; |
void _exit(int status) |
{ |
thread_exit(status); |
} |
void __main(void) |
{ |
psthread_data_t *pt; |
(void) as_area_create(&_heap, 1, AS_AREA_WRITE | AS_AREA_READ); |
_async_init(); |
pt = psthread_setup(); |
__tcb_set(pt->tcb); |
} |
void __io_init(void) |
{ |
open("stdin", 0); |
open("stdout", 0); |
open("stderr", 0); |
} |
void __exit(void) |
{ |
psthread_teardown(__tcb_get()->pst_data); |
_exit(0); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/psthread.c |
---|
0,0 → 1,308 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 <libadt/list.h> |
#include <psthread.h> |
#include <malloc.h> |
#include <unistd.h> |
#include <thread.h> |
#include <stdio.h> |
#include <libarch/faddr.h> |
#include <futex.h> |
#include <assert.h> |
#include <async.h> |
#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO |
#define PSTHREAD_INITIAL_STACK_PAGES_NO 1 |
#endif |
static LIST_INITIALIZE(ready_list); |
static LIST_INITIALIZE(serialized_list); |
static LIST_INITIALIZE(manager_list); |
static void psthread_main(void); |
static atomic_t psthread_futex = FUTEX_INITIALIZER; |
/** Count of real threads that are in async_serialized mode */ |
static int serialized_threads; /* Protected by async_futex */ |
/** Thread-local count of serialization. If >0, we must not preempt */ |
static __thread int serialization_count; |
/** Counter of threads residing in async_manager */ |
static int threads_in_manager; |
/** Setup psthread information into TCB structure */ |
psthread_data_t * psthread_setup() |
{ |
psthread_data_t *pt; |
tcb_t *tcb; |
tcb = __make_tls(); |
if (!tcb) |
return NULL; |
pt = malloc(sizeof(*pt)); |
if (!pt) { |
__free_tls(tcb); |
return NULL; |
} |
tcb->pst_data = pt; |
pt->tcb = tcb; |
return pt; |
} |
void psthread_teardown(psthread_data_t *pt) |
{ |
__free_tls(pt->tcb); |
free(pt); |
} |
/** Function that is called on entry to new pseudo thread */ |
void psthread_main(void) |
{ |
psthread_data_t *pt = __tcb_get()->pst_data; |
pt->retval = pt->func(pt->arg); |
pt->finished = 1; |
if (pt->waiter) |
list_append(&pt->waiter->link, &ready_list); |
psthread_schedule_next_adv(PS_FROM_DEAD); |
} |
/** Schedule next userspace pseudo thread. |
* |
* If calling with PS_TO_MANAGER parameter, the async_futex should be |
* held. |
* |
* @param ctype Type of switch. |
* @return 0 if there is no ready pseudo thread, 1 otherwise. |
*/ |
int psthread_schedule_next_adv(pschange_type ctype) |
{ |
psthread_data_t *srcpt, *dstpt; |
int retval = 0; |
futex_down(&psthread_futex); |
if (ctype == PS_PREEMPT && list_empty(&ready_list)) |
goto ret_0; |
if (ctype == PS_FROM_MANAGER) { |
if (list_empty(&ready_list) && list_empty(&serialized_list)) |
goto ret_0; |
/* Do not preempt if there is not sufficient count of thread managers */ |
if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) { |
goto ret_0; |
} |
} |
/* If we are going to manager and none exists, create it */ |
if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) { |
while (list_empty(&manager_list)) { |
futex_up(&psthread_futex); |
async_create_manager(); |
futex_down(&psthread_futex); |
} |
} |
if (ctype != PS_FROM_DEAD) { |
/* Save current state */ |
srcpt = __tcb_get()->pst_data; |
if (!context_save(&srcpt->ctx)) { |
if (serialization_count) |
srcpt->flags &= ~PSTHREAD_SERIALIZED; |
return 1; // futex_up already done here |
} |
/* Save myself to correct run list */ |
if (ctype == PS_PREEMPT) |
list_append(&srcpt->link, &ready_list); |
else if (ctype == PS_FROM_MANAGER) { |
list_append(&srcpt->link, &manager_list); |
threads_in_manager--; |
} /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should |
* already be somewhere, or we will be lost */ |
} else |
srcpt = NULL; /* Avoid GCC warning, if ctype == PS_FROM_DEAD, srcpt is not used */ |
/* Choose new thread to run */ |
if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) { |
dstpt = list_get_instance(manager_list.next,psthread_data_t, link); |
if (serialization_count && ctype == PS_TO_MANAGER) { |
serialized_threads++; |
srcpt->flags |= PSTHREAD_SERIALIZED; |
} |
threads_in_manager++; |
} else { |
if (!list_empty(&serialized_list)) { |
dstpt = list_get_instance(serialized_list.next, psthread_data_t, link); |
serialized_threads--; |
} else |
dstpt = list_get_instance(ready_list.next, psthread_data_t, link); |
} |
list_remove(&dstpt->link); |
futex_up(&psthread_futex); |
context_restore(&dstpt->ctx); |
ret_0: |
futex_up(&psthread_futex); |
return retval; |
} |
/** Wait for uspace pseudo thread to finish. |
* |
* @param psthrid Pseudo thread to wait for. |
* |
* @return Value returned by the finished thread. |
*/ |
int psthread_join(pstid_t psthrid) |
{ |
volatile psthread_data_t *pt; |
volatile int retval; |
/* Handle psthrid = Kernel address -> it is wait for call */ |
pt = (psthread_data_t *) psthrid; |
/* TODO */ |
printf("join unsupported\n"); |
_exit(1); |
retval = pt->retval; |
free(pt->stack); |
psthread_teardown((void *)pt); |
return retval; |
} |
/** Create a userspace pseudo thread. |
* |
* @param func Pseudo thread function. |
* @param arg Argument to pass to func. |
* |
* @return 0 on failure, TLS of the new pseudo thread. |
*/ |
pstid_t psthread_create(int (*func)(void *), void *arg) |
{ |
psthread_data_t *pt; |
pt = psthread_setup(); |
if (!pt) |
return 0; |
pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize()); |
if (!pt->stack) { |
psthread_teardown(pt); |
return 0; |
} |
pt->arg= arg; |
pt->func = func; |
pt->finished = 0; |
pt->waiter = NULL; |
pt->flags = 0; |
context_save(&pt->ctx); |
context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb); |
return (pstid_t )pt; |
} |
/** Add a thread to ready list */ |
void psthread_add_ready(pstid_t psthrid) |
{ |
psthread_data_t *pt; |
pt = (psthread_data_t *) psthrid; |
futex_down(&psthread_futex); |
if ((pt->flags & PSTHREAD_SERIALIZED)) |
list_append(&pt->link, &serialized_list); |
else |
list_append(&pt->link, &ready_list); |
futex_up(&psthread_futex); |
} |
/** Add a thread to manager list */ |
void psthread_add_manager(pstid_t psthrid) |
{ |
psthread_data_t *pt; |
pt = (psthread_data_t *) psthrid; |
futex_down(&psthread_futex); |
list_append(&pt->link, &manager_list); |
futex_up(&psthread_futex); |
} |
/** Remove one manager from manager list */ |
void psthread_remove_manager() |
{ |
futex_down(&psthread_futex); |
if (list_empty(&manager_list)) { |
futex_up(&psthread_futex); |
return; |
} |
list_remove(manager_list.next); |
futex_up(&psthread_futex); |
} |
/** Return thread id of current running thread */ |
pstid_t psthread_get_id(void) |
{ |
return (pstid_t)__tcb_get()->pst_data; |
} |
/** Disable preemption |
* |
* If the thread wants to send several message in row and does not want |
* to be preempted, it should start async_serialize_start() in the beginning |
* of communication and async_serialize_end() in the end. If it is a |
* true multithreaded application, it should protect the communication channel |
* by a futex as well. Interrupt messages will can still be preempted. |
*/ |
void psthread_inc_sercount(void) |
{ |
serialization_count++; |
} |
void psthread_dec_sercount(void) |
{ |
serialization_count--; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/ipc.c |
---|
0,0 → 1,484 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 |
* @{ |
* @} |
*/ |
/** @addtogroup libcipc IPC |
* @brief HelenOS uspace IPC |
* @{ |
* @ingroup libc |
*/ |
/** @file |
*/ |
#include <ipc/ipc.h> |
#include <libc.h> |
#include <malloc.h> |
#include <errno.h> |
#include <libadt/list.h> |
#include <stdio.h> |
#include <unistd.h> |
#include <futex.h> |
#include <kernel/synch/synch.h> |
#include <async.h> |
#include <psthread.h> |
/** Structure used for keeping track of sent async msgs |
* and queing unsent msgs |
* |
*/ |
typedef struct { |
link_t list; |
ipc_async_callback_t callback; |
void *private; |
union { |
ipc_callid_t callid; |
struct { |
ipc_call_t data; |
int phoneid; |
} msg; |
}u; |
pstid_t ptid; /**< Thread waiting for sending this msg */ |
} async_call_t; |
LIST_INITIALIZE(dispatched_calls); |
/* queued_calls is protcted by async_futex, because if the |
* call cannot be sent into kernel, async framework is used |
* automatically |
*/ |
LIST_INITIALIZE(queued_calls); /**< List of async calls that were not accepted |
* by kernel */ |
static atomic_t ipc_futex = FUTEX_INITIALIZER; |
int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1, |
ipcarg_t *result) |
{ |
ipc_call_t resdata; |
int callres; |
callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1, |
(sysarg_t)&resdata); |
if (callres) |
return callres; |
if (result) |
*result = IPC_GET_ARG1(resdata); |
return IPC_GET_RETVAL(resdata); |
} |
int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1, |
ipcarg_t arg2, ipcarg_t arg3, |
ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3) |
{ |
ipc_call_t data; |
int callres; |
IPC_SET_METHOD(data, method); |
IPC_SET_ARG1(data, arg1); |
IPC_SET_ARG2(data, arg2); |
IPC_SET_ARG3(data, arg3); |
callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t)&data, |
(sysarg_t)&data); |
if (callres) |
return callres; |
if (result1) |
*result1 = IPC_GET_ARG1(data); |
if (result2) |
*result2 = IPC_GET_ARG2(data); |
if (result3) |
*result3 = IPC_GET_ARG3(data); |
return IPC_GET_RETVAL(data); |
} |
/** Syscall to send asynchronous message */ |
static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data) |
{ |
return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t)data); |
} |
/** Prolog to ipc_async_send functions */ |
static inline async_call_t *ipc_prepare_async(void *private, ipc_async_callback_t callback) |
{ |
async_call_t *call; |
call = malloc(sizeof(*call)); |
if (!call) { |
if (callback) |
callback(private, ENOMEM, NULL); |
return NULL; |
} |
call->callback = callback; |
call->private = private; |
return call; |
} |
/** Epilogue of ipc_async_send functions */ |
static inline void ipc_finish_async(ipc_callid_t callid, int phoneid, |
async_call_t *call, int can_preempt) |
{ |
if (!call) { /* Nothing to do regardless if failed or not */ |
futex_up(&ipc_futex); |
return; |
} |
if (callid == IPC_CALLRET_FATAL) { |
futex_up(&ipc_futex); |
/* Call asynchronous handler with error code */ |
if (call->callback) |
call->callback(call->private, ENOENT, NULL); |
free(call); |
return; |
} |
if (callid == IPC_CALLRET_TEMPORARY) { |
futex_up(&ipc_futex); |
call->u.msg.phoneid = phoneid; |
futex_down(&async_futex); |
list_append(&call->list, &queued_calls); |
if (can_preempt) { |
call->ptid = psthread_get_id(); |
psthread_schedule_next_adv(PS_TO_MANAGER); |
/* Async futex unlocked by previous call */ |
} else { |
call->ptid = 0; |
futex_up(&async_futex); |
} |
return; |
} |
call->u.callid = callid; |
/* Add call to list of dispatched calls */ |
list_append(&call->list, &dispatched_calls); |
futex_up(&ipc_futex); |
} |
/** Send asynchronous message |
* |
* - if fatal error, call callback handler with proper error code |
* - if message cannot be temporarily sent, add to queue |
*/ |
void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1, |
ipcarg_t arg2, void *private, |
ipc_async_callback_t callback, int can_preempt) |
{ |
async_call_t *call = NULL; |
ipc_callid_t callid; |
if (callback) { |
call = ipc_prepare_async(private, callback); |
if (!call) |
return; |
} |
/* We need to make sure that we get callid before |
* another thread accesses the queue again */ |
futex_down(&ipc_futex); |
callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1, arg2); |
if (callid == IPC_CALLRET_TEMPORARY) { |
if (!call) { |
call = ipc_prepare_async(private, callback); |
if (!call) |
return; |
} |
IPC_SET_METHOD(call->u.msg.data, method); |
IPC_SET_ARG1(call->u.msg.data, arg1); |
IPC_SET_ARG2(call->u.msg.data, arg2); |
} |
ipc_finish_async(callid, phoneid, call, can_preempt); |
} |
/** Send asynchronous message |
* |
* - if fatal error, call callback handler with proper error code |
* - if message cannot be temporarily sent, add to queue |
*/ |
void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1, |
ipcarg_t arg2, ipcarg_t arg3, void *private, |
ipc_async_callback_t callback, int can_preempt) |
{ |
async_call_t *call; |
ipc_callid_t callid; |
call = ipc_prepare_async(private, callback); |
if (!call) |
return; |
IPC_SET_METHOD(call->u.msg.data, method); |
IPC_SET_ARG1(call->u.msg.data, arg1); |
IPC_SET_ARG2(call->u.msg.data, arg2); |
IPC_SET_ARG3(call->u.msg.data, arg3); |
/* We need to make sure that we get callid before |
* another thread accesses the queue again */ |
futex_down(&ipc_futex); |
callid = _ipc_call_async(phoneid, &call->u.msg.data); |
ipc_finish_async(callid, phoneid, call, can_preempt); |
} |
/** Send a fast answer to a received call. |
* |
* The fast answer makes use of passing retval and first two arguments in registers. |
* If you need to return more, use the ipc_answer() instead. |
* |
* @param callid ID of the call being answered. |
* @param retval Return value. |
* @param arg1 First return argument. |
* @param arg2 Second return argument. |
* |
* @return Zero on success or a value from @ref errno.h on failure. |
*/ |
ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1, |
ipcarg_t arg2) |
{ |
return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2); |
} |
/** Send a full answer to a received call. |
* |
* @param callid ID of the call being answered. |
* @param call Call data. Must be already initialized by the responder. |
* |
* @return Zero on success or a value from @ref errno.h on failure. |
*/ |
ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call) |
{ |
return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call); |
} |
/** Try to dispatch queed calls from async queue */ |
static void try_dispatch_queued_calls(void) |
{ |
async_call_t *call; |
ipc_callid_t callid; |
/* TODO: integrate intelligently ipc_futex, so that it |
* is locked during ipc_call_async, until it is added |
* to dispatched_calls |
*/ |
futex_down(&async_futex); |
while (!list_empty(&queued_calls)) { |
call = list_get_instance(queued_calls.next, async_call_t, |
list); |
callid = _ipc_call_async(call->u.msg.phoneid, |
&call->u.msg.data); |
if (callid == IPC_CALLRET_TEMPORARY) { |
break; |
} |
list_remove(&call->list); |
futex_up(&async_futex); |
if (call->ptid) |
psthread_add_ready(call->ptid); |
if (callid == IPC_CALLRET_FATAL) { |
if (call->callback) |
call->callback(call->private, ENOENT, NULL); |
free(call); |
} else { |
call->u.callid = callid; |
futex_down(&ipc_futex); |
list_append(&call->list, &dispatched_calls); |
futex_up(&ipc_futex); |
} |
futex_down(&async_futex); |
} |
futex_up(&async_futex); |
} |
/** Handle received answer |
* |
* TODO: Make it use hash table |
* |
* @param callid Callid (with first bit set) of the answered call |
*/ |
static void handle_answer(ipc_callid_t callid, ipc_call_t *data) |
{ |
link_t *item; |
async_call_t *call; |
callid &= ~IPC_CALLID_ANSWERED; |
futex_down(&ipc_futex); |
for (item = dispatched_calls.next; item != &dispatched_calls; |
item = item->next) { |
call = list_get_instance(item, async_call_t, list); |
if (call->u.callid == callid) { |
list_remove(&call->list); |
futex_up(&ipc_futex); |
if (call->callback) |
call->callback(call->private, |
IPC_GET_RETVAL(*data), |
data); |
free(call); |
return; |
} |
} |
futex_up(&ipc_futex); |
/* We may get here after async_msg, which doesn't register any callback */ |
} |
/** One cycle of ipc wait for call call |
* |
* - dispatch ASYNC reoutines in the background |
* @param call Space where the message is stored |
* @param usec Timeout in microseconds |
* @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking) |
* @return Callid of the answer. |
*/ |
ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags) |
{ |
ipc_callid_t callid; |
callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags); |
/* Handle received answers */ |
if (callid & IPC_CALLID_ANSWERED) { |
handle_answer(callid, call); |
try_dispatch_queued_calls(); |
} |
return callid; |
} |
/** Wait some time for an IPC call. |
* |
* - dispatch ASYNC reoutines in the background |
* |
* @param call Space where the message is stored |
* @param usec Timeout in microseconds. |
* @return Callid of the answer. |
*/ |
ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec) |
{ |
ipc_callid_t callid; |
do { |
callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE); |
} while (callid & IPC_CALLID_ANSWERED); |
return callid; |
} |
/** Check if there is an IPC call waiting to be picked up. |
* |
* - dispatch ASYNC reoutines in the background |
* |
* @param call Space where the message is stored |
* @return Callid of the answer. |
*/ |
ipc_callid_t ipc_trywait_for_call(ipc_call_t *call) |
{ |
ipc_callid_t callid; |
do { |
callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING); |
} while (callid & IPC_CALLID_ANSWERED); |
return callid; |
} |
/** Ask destination to do a callback connection |
* |
* @return 0 - OK, error code |
*/ |
int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phone) |
{ |
return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1, |
arg2, 0, 0, 0, phone); |
} |
/** Ask through phone for a new connection to some service |
* |
* @return new phoneid - OK, error code |
*/ |
int ipc_connect_me_to(int phoneid, int arg1, int arg2) |
{ |
ipcarg_t newphid; |
int res; |
res = ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1, |
arg2, 0, 0, 0, &newphid); |
if (res) |
return res; |
return newphid; |
} |
/* Hang up specified phone */ |
int ipc_hangup(int phoneid) |
{ |
return __SYSCALL1(SYS_IPC_HANGUP, phoneid); |
} |
/** Register IRQ notification. |
* |
* @param inr IRQ number. |
* @param devno Device number of the device generating inr. |
* @param method Use this method for notifying me. |
* @param ucode Top-half pseudocode handler. |
* |
* @return Value returned by the kernel. |
*/ |
int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode) |
{ |
return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method, (sysarg_t) ucode); |
} |
/** Unregister IRQ notification. |
* |
* @param inr IRQ number. |
* @param devno Device number of the device generating inr. |
* |
* @return Value returned by the kernel. |
*/ |
int ipc_unregister_irq(int inr, int devno) |
{ |
return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno); |
} |
int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1) |
{ |
return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/async.c |
---|
0,0 → 1,811 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 |
*/ |
/** |
* Asynchronous library |
* |
* The aim of this library is facilitating writing programs utilizing |
* the asynchronous nature of HelenOS IPC, yet using a normal way |
* of programming. |
* |
* You should be able to write very simple multithreaded programs, |
* the async framework will automatically take care of most synchronization |
* problems. |
* |
* Default semantics: |
* - send() - send asynchronously. If the kernel refuses to send more |
* messages, [ try to get responses from kernel, if nothing |
* found, might try synchronous ] |
* |
* Example of use: |
* |
* 1) Multithreaded client application |
* create_thread(thread1); |
* create_thread(thread2); |
* ... |
* |
* thread1() { |
* conn = ipc_connect_me_to(); |
* c1 = send(conn); |
* c2 = send(conn); |
* wait_for(c1); |
* wait_for(c2); |
* } |
* |
* |
* 2) Multithreaded server application |
* main() { |
* async_manager(); |
* } |
* |
* |
* client_connection(icallid, *icall) { |
* if (want_refuse) { |
* ipc_answer_fast(icallid, ELIMIT, 0, 0); |
* return; |
* } |
* ipc_answer_fast(icallid, 0, 0, 0); |
* |
* callid = async_get_call(&call); |
* handle(callid, call); |
* ipc_answer_fast(callid, 1, 2, 3); |
* |
* callid = async_get_call(&call); |
* .... |
* } |
* |
* TODO: Detaching/joining dead psthreads? |
*/ |
#include <futex.h> |
#include <async.h> |
#include <psthread.h> |
#include <stdio.h> |
#include <libadt/hash_table.h> |
#include <libadt/list.h> |
#include <ipc/ipc.h> |
#include <assert.h> |
#include <errno.h> |
#include <time.h> |
#include <arch/barrier.h> |
atomic_t async_futex = FUTEX_INITIALIZER; |
static hash_table_t conn_hash_table; |
static LIST_INITIALIZE(timeout_list); |
typedef struct { |
struct timeval expires; /**< Expiration time for waiting thread */ |
int inlist; /**< If true, this struct is in timeout list */ |
link_t link; |
pstid_t ptid; /**< Thread waiting for this message */ |
int active; /**< If this thread is currently active */ |
int timedout; /**< If true, we timed out */ |
} awaiter_t; |
typedef struct { |
awaiter_t wdata; |
int done; /**< If reply was received */ |
ipc_call_t *dataptr; /**< Pointer where the answer data |
* is stored */ |
ipcarg_t retval; |
} amsg_t; |
typedef struct { |
link_t link; |
ipc_callid_t callid; |
ipc_call_t call; |
} msg_t; |
typedef struct { |
awaiter_t wdata; |
link_t link; /**< Hash table link */ |
ipcarg_t in_phone_hash; /**< Incoming phone hash. */ |
link_t msg_queue; /**< Messages that should be delivered to this thread */ |
/* Structures for connection opening packet */ |
ipc_callid_t callid; |
ipc_call_t call; |
ipc_callid_t close_callid; /* Identification of closing packet */ |
void (*cthread)(ipc_callid_t,ipc_call_t *); |
} connection_t; |
/** Identifier of incoming connection handled by current thread */ |
__thread connection_t *PS_connection; |
/** If true, it is forbidden to use async_req functions and |
* all preemption is disabled */ |
__thread int in_interrupt_handler; |
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call); |
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call); |
static async_client_conn_t client_connection = default_client_connection; |
static async_client_conn_t interrupt_received = default_interrupt_received; |
/** Add microseconds to give timeval */ |
static void tv_add(struct timeval *tv, suseconds_t usecs) |
{ |
tv->tv_sec += usecs / 1000000; |
tv->tv_usec += usecs % 1000000; |
if (tv->tv_usec > 1000000) { |
tv->tv_sec++; |
tv->tv_usec -= 1000000; |
} |
} |
/** Subtract 2 timevals, return microseconds difference */ |
static suseconds_t tv_sub(struct timeval *tv1, struct timeval *tv2) |
{ |
suseconds_t result; |
result = tv1->tv_usec - tv2->tv_usec; |
result += (tv1->tv_sec - tv2->tv_sec) * 1000000; |
return result; |
} |
/** Compare timeval |
* |
* @return 1 if tv1 > tv2, otherwise 0 |
*/ |
static int tv_gt(struct timeval *tv1, struct timeval *tv2) |
{ |
if (tv1->tv_sec > tv2->tv_sec) |
return 1; |
if (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec > tv2->tv_usec) |
return 1; |
return 0; |
} |
static int tv_gteq(struct timeval *tv1, struct timeval *tv2) |
{ |
if (tv1->tv_sec > tv2->tv_sec) |
return 1; |
if (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec >= tv2->tv_usec) |
return 1; |
return 0; |
} |
/* Hash table functions */ |
#define CONN_HASH_TABLE_CHAINS 32 |
static hash_index_t conn_hash(unsigned long *key) |
{ |
assert(key); |
return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS; |
} |
static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item) |
{ |
connection_t *hs; |
hs = hash_table_get_instance(item, connection_t, link); |
return key[0] == hs->in_phone_hash; |
} |
static void conn_remove(link_t *item) |
{ |
free(hash_table_get_instance(item, connection_t, link)); |
} |
/** Operations for NS hash table. */ |
static hash_table_operations_t conn_hash_table_ops = { |
.hash = conn_hash, |
.compare = conn_compare, |
.remove_callback = conn_remove |
}; |
/** Insert sort timeout msg into timeouts list |
* |
*/ |
static void insert_timeout(awaiter_t *wd) |
{ |
link_t *tmp; |
awaiter_t *cur; |
wd->timedout = 0; |
wd->inlist = 1; |
tmp = timeout_list.next; |
while (tmp != &timeout_list) { |
cur = list_get_instance(tmp, awaiter_t, link); |
if (tv_gteq(&cur->expires, &wd->expires)) |
break; |
tmp = tmp->next; |
} |
list_append(&wd->link, tmp); |
} |
/*************************************************/ |
/** Try to route a call to an appropriate connection thread |
* |
*/ |
static int route_call(ipc_callid_t callid, ipc_call_t *call) |
{ |
connection_t *conn; |
msg_t *msg; |
link_t *hlp; |
unsigned long key; |
futex_down(&async_futex); |
key = call->in_phone_hash; |
hlp = hash_table_find(&conn_hash_table, &key); |
if (!hlp) { |
futex_up(&async_futex); |
return 0; |
} |
conn = hash_table_get_instance(hlp, connection_t, link); |
msg = malloc(sizeof(*msg)); |
msg->callid = callid; |
msg->call = *call; |
list_append(&msg->link, &conn->msg_queue); |
if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP) |
conn->close_callid = callid; |
/* If the call is waiting for event, run it */ |
if (!conn->wdata.active) { |
/* If in timeout list, remove it */ |
if (conn->wdata.inlist) { |
conn->wdata.inlist = 0; |
list_remove(&conn->wdata.link); |
} |
conn->wdata.active = 1; |
psthread_add_ready(conn->wdata.ptid); |
} |
futex_up(&async_futex); |
return 1; |
} |
/** Return new incoming message for current(thread-local) connection */ |
ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs) |
{ |
msg_t *msg; |
ipc_callid_t callid; |
connection_t *conn; |
assert(PS_connection); |
/* GCC 4.1.0 coughs on PS_connection-> dereference, |
* GCC 4.1.1 happilly puts the rdhwr instruction in delay slot. |
* I would never expect to find so many errors in |
* compiler *($&$(*&$ |
*/ |
conn = PS_connection; |
futex_down(&async_futex); |
if (usecs) { |
gettimeofday(&conn->wdata.expires, NULL); |
tv_add(&conn->wdata.expires, usecs); |
} else { |
conn->wdata.inlist = 0; |
} |
/* If nothing in queue, wait until something appears */ |
while (list_empty(&conn->msg_queue)) { |
if (usecs) |
insert_timeout(&conn->wdata); |
conn->wdata.active = 0; |
psthread_schedule_next_adv(PS_TO_MANAGER); |
/* Futex is up after getting back from async_manager |
* get it again */ |
futex_down(&async_futex); |
if (usecs && conn->wdata.timedout && \ |
list_empty(&conn->msg_queue)) { |
/* If we timed out-> exit */ |
futex_up(&async_futex); |
return 0; |
} |
} |
msg = list_get_instance(conn->msg_queue.next, msg_t, link); |
list_remove(&msg->link); |
callid = msg->callid; |
*call = msg->call; |
free(msg); |
futex_up(&async_futex); |
return callid; |
} |
/** Thread function that gets created on new connection |
* |
* This function is defined as a weak symbol - to be redefined in |
* user code. |
*/ |
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call) |
{ |
ipc_answer_fast(callid, ENOENT, 0, 0); |
} |
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call) |
{ |
} |
/** Wrapper for client connection thread |
* |
* When new connection arrives, thread with this function is created. |
* It calls client_connection and does final cleanup. |
* |
* @param arg Connection structure pointer |
*/ |
static int connection_thread(void *arg) |
{ |
unsigned long key; |
msg_t *msg; |
int close_answered = 0; |
/* Setup thread local connection pointer */ |
PS_connection = (connection_t *)arg; |
PS_connection->cthread(PS_connection->callid, &PS_connection->call); |
/* Remove myself from connection hash table */ |
futex_down(&async_futex); |
key = PS_connection->in_phone_hash; |
hash_table_remove(&conn_hash_table, &key, 1); |
futex_up(&async_futex); |
/* Answer all remaining messages with ehangup */ |
while (!list_empty(&PS_connection->msg_queue)) { |
msg = list_get_instance(PS_connection->msg_queue.next, msg_t, link); |
list_remove(&msg->link); |
if (msg->callid == PS_connection->close_callid) |
close_answered = 1; |
ipc_answer_fast(msg->callid, EHANGUP, 0, 0); |
free(msg); |
} |
if (PS_connection->close_callid) |
ipc_answer_fast(PS_connection->close_callid, 0, 0, 0); |
return 0; |
} |
/** Create new thread for a new connection |
* |
* Creates new thread for connection, fills in connection |
* structures and inserts it into the hash table, so that |
* later we can easily do routing of messages to particular |
* threads. |
* |
* @param in_phone_hash Identification of the incoming connection |
* @param callid Callid of the IPC_M_CONNECT_ME_TO packet |
* @param call Call data of the opening packet |
* @param cthread Thread function that should be called upon |
* opening the connection |
* @return New thread id |
*/ |
pstid_t async_new_connection(ipcarg_t in_phone_hash,ipc_callid_t callid, ipc_call_t *call, void (*cthread)(ipc_callid_t, ipc_call_t *)) |
{ |
connection_t *conn; |
unsigned long key; |
conn = malloc(sizeof(*conn)); |
if (!conn) { |
ipc_answer_fast(callid, ENOMEM, 0, 0); |
return NULL; |
} |
conn->in_phone_hash = in_phone_hash; |
list_initialize(&conn->msg_queue); |
conn->callid = callid; |
conn->close_callid = 0; |
if (call) |
conn->call = *call; |
conn->wdata.active = 1; /* We will activate it asap */ |
conn->cthread = cthread; |
conn->wdata.ptid = psthread_create(connection_thread, conn); |
if (!conn->wdata.ptid) { |
free(conn); |
ipc_answer_fast(callid, ENOMEM, 0, 0); |
return NULL; |
} |
/* Add connection to hash table */ |
key = conn->in_phone_hash; |
futex_down(&async_futex); |
hash_table_insert(&conn_hash_table, &key, &conn->link); |
futex_up(&async_futex); |
psthread_add_ready(conn->wdata.ptid); |
return conn->wdata.ptid; |
} |
/** Handle call that was received */ |
static void handle_call(ipc_callid_t callid, ipc_call_t *call) |
{ |
/* Unrouted call - do some default behaviour */ |
if ((callid & IPC_CALLID_NOTIFICATION)) { |
in_interrupt_handler = 1; |
(*interrupt_received)(callid,call); |
in_interrupt_handler = 0; |
return; |
} |
switch (IPC_GET_METHOD(*call)) { |
case IPC_M_CONNECT_ME_TO: |
/* Open new connection with thread etc. */ |
async_new_connection(IPC_GET_ARG3(*call), callid, call, client_connection); |
return; |
} |
/* Try to route call through connection tables */ |
if (route_call(callid, call)) |
return; |
/* Unknown call from unknown phone - hang it up */ |
ipc_answer_fast(callid, EHANGUP, 0, 0); |
} |
/** Fire all timeouts that expired |
* |
*/ |
static void handle_expired_timeouts(void) |
{ |
struct timeval tv; |
awaiter_t *waiter; |
link_t *cur; |
gettimeofday(&tv,NULL); |
futex_down(&async_futex); |
cur = timeout_list.next; |
while (cur != &timeout_list) { |
waiter = list_get_instance(cur,awaiter_t,link); |
if (tv_gt(&waiter->expires, &tv)) |
break; |
cur = cur->next; |
list_remove(&waiter->link); |
waiter->inlist = 0; |
waiter->timedout = 1; |
/* Redundant condition? The thread should not |
* be active when it gets here. |
*/ |
if (!waiter->active) { |
waiter->active = 1; |
psthread_add_ready(waiter->ptid); |
} |
} |
futex_up(&async_futex); |
} |
/** Endless loop dispatching incoming calls and answers */ |
static int async_manager_worker(void) |
{ |
ipc_call_t call; |
ipc_callid_t callid; |
int timeout; |
awaiter_t *waiter; |
struct timeval tv; |
while (1) { |
if (psthread_schedule_next_adv(PS_FROM_MANAGER)) { |
futex_up(&async_futex); |
/* async_futex is always held |
* when entering manager thread |
*/ |
continue; |
} |
futex_down(&async_futex); |
if (!list_empty(&timeout_list)) { |
waiter = list_get_instance(timeout_list.next,awaiter_t,link); |
gettimeofday(&tv,NULL); |
if (tv_gteq(&tv, &waiter->expires)) { |
futex_up(&async_futex); |
handle_expired_timeouts(); |
continue; |
} else |
timeout = tv_sub(&waiter->expires, &tv); |
} else |
timeout = SYNCH_NO_TIMEOUT; |
futex_up(&async_futex); |
callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE); |
if (!callid) { |
handle_expired_timeouts(); |
continue; |
} |
if (callid & IPC_CALLID_ANSWERED) { |
continue; |
} |
handle_call(callid, &call); |
} |
return 0; |
} |
/** Function to start async_manager as a standalone thread |
* |
* When more kernel threads are used, one async manager should |
* exist per thread. The particular implementation may change, |
* currently one async_manager is started automatically per kernel |
* thread except main thread. |
*/ |
static int async_manager_thread(void *arg) |
{ |
futex_up(&async_futex); |
/* async_futex is always locked when entering |
* manager */ |
async_manager_worker(); |
return 0; |
} |
/** Add one manager to manager list */ |
void async_create_manager(void) |
{ |
pstid_t ptid; |
ptid = psthread_create(async_manager_thread, NULL); |
psthread_add_manager(ptid); |
} |
/** Remove one manager from manager list */ |
void async_destroy_manager(void) |
{ |
psthread_remove_manager(); |
} |
/** Initialize internal structures needed for async manager */ |
int _async_init(void) |
{ |
if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1, &conn_hash_table_ops)) { |
printf("%s: cannot create hash table\n", "async"); |
return ENOMEM; |
} |
return 0; |
} |
/** IPC handler for messages in async framework |
* |
* Notify thread that is waiting for this message, that it arrived |
*/ |
static void reply_received(void *private, int retval, |
ipc_call_t *data) |
{ |
amsg_t *msg = (amsg_t *) private; |
msg->retval = retval; |
futex_down(&async_futex); |
/* Copy data after futex_down, just in case the |
* call was detached |
*/ |
if (msg->dataptr) |
*msg->dataptr = *data; |
write_barrier(); |
/* Remove message from timeout list */ |
if (msg->wdata.inlist) |
list_remove(&msg->wdata.link); |
msg->done = 1; |
if (! msg->wdata.active) { |
msg->wdata.active = 1; |
psthread_add_ready(msg->wdata.ptid); |
} |
futex_up(&async_futex); |
} |
/** Send message and return id of the sent message |
* |
* The return value can be used as input for async_wait() to wait |
* for completion. |
*/ |
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2, |
ipc_call_t *dataptr) |
{ |
amsg_t *msg; |
if (in_interrupt_handler) { |
printf("Cannot send asynchronous request in interrupt handler.\n"); |
_exit(1); |
} |
msg = malloc(sizeof(*msg)); |
msg->done = 0; |
msg->dataptr = dataptr; |
msg->wdata.active = 1; /* We may sleep in next method, but it |
* will use it's own mechanism */ |
ipc_call_async_2(phoneid,method,arg1,arg2,msg,reply_received,1); |
return (aid_t) msg; |
} |
/** Send message and return id of the sent message |
* |
* The return value can be used as input for async_wait() to wait |
* for completion. |
*/ |
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2, |
ipcarg_t arg3, ipc_call_t *dataptr) |
{ |
amsg_t *msg; |
if (in_interrupt_handler) { |
printf("Cannot send asynchronous request in interrupt handler.\n"); |
_exit(1); |
} |
msg = malloc(sizeof(*msg)); |
msg->done = 0; |
msg->dataptr = dataptr; |
msg->wdata.active = 1; /* We may sleep in next method, but it |
* will use it's own mechanism */ |
ipc_call_async_3(phoneid,method,arg1,arg2,arg3, msg,reply_received,1); |
return (aid_t) msg; |
} |
/** Wait for a message sent by async framework |
* |
* @param amsgid Message ID to wait for |
* @param retval Pointer to variable where will be stored retval |
* of the answered message. If NULL, it is ignored. |
* |
*/ |
void async_wait_for(aid_t amsgid, ipcarg_t *retval) |
{ |
amsg_t *msg = (amsg_t *) amsgid; |
futex_down(&async_futex); |
if (msg->done) { |
futex_up(&async_futex); |
goto done; |
} |
msg->wdata.ptid = psthread_get_id(); |
msg->wdata.active = 0; |
msg->wdata.inlist = 0; |
/* Leave locked async_futex when entering this function */ |
psthread_schedule_next_adv(PS_TO_MANAGER); |
/* futex is up automatically after psthread_schedule_next...*/ |
done: |
if (retval) |
*retval = msg->retval; |
free(msg); |
} |
/** Wait for a message sent by async framework with timeout |
* |
* @param amsgid Message ID to wait for |
* @param retval Pointer to variable where will be stored retval |
* of the answered message. If NULL, it is ignored. |
* @param timeout Timeout in usecs |
* @return 0 on success, ETIMEOUT if timeout expired |
* |
*/ |
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout) |
{ |
amsg_t *msg = (amsg_t *) amsgid; |
/* TODO: Let it go through the event read at least once */ |
if (timeout < 0) |
return ETIMEOUT; |
futex_down(&async_futex); |
if (msg->done) { |
futex_up(&async_futex); |
goto done; |
} |
gettimeofday(&msg->wdata.expires, NULL); |
tv_add(&msg->wdata.expires, timeout); |
msg->wdata.ptid = psthread_get_id(); |
msg->wdata.active = 0; |
insert_timeout(&msg->wdata); |
/* Leave locked async_futex when entering this function */ |
psthread_schedule_next_adv(PS_TO_MANAGER); |
/* futex is up automatically after psthread_schedule_next...*/ |
if (!msg->done) |
return ETIMEOUT; |
done: |
if (retval) |
*retval = msg->retval; |
free(msg); |
return 0; |
} |
/** Wait specified time, but in the meantime handle incoming events |
* |
* @param timeout Time in microseconds to wait |
*/ |
void async_usleep(suseconds_t timeout) |
{ |
amsg_t *msg; |
if (in_interrupt_handler) { |
printf("Cannot call async_usleep in interrupt handler.\n"); |
_exit(1); |
} |
msg = malloc(sizeof(*msg)); |
if (!msg) |
return; |
msg->wdata.ptid = psthread_get_id(); |
msg->wdata.active = 0; |
gettimeofday(&msg->wdata.expires, NULL); |
tv_add(&msg->wdata.expires, timeout); |
futex_down(&async_futex); |
insert_timeout(&msg->wdata); |
/* Leave locked async_futex when entering this function */ |
psthread_schedule_next_adv(PS_TO_MANAGER); |
/* futex is up automatically after psthread_schedule_next...*/ |
free(msg); |
} |
/** Set function that is called, IPC_M_CONNECT_ME_TO is received |
* |
* @param conn Function that will form new psthread. |
*/ |
void async_set_client_connection(async_client_conn_t conn) |
{ |
client_connection = conn; |
} |
void async_set_interrupt_received(async_client_conn_t conn) |
{ |
interrupt_received = conn; |
} |
/* Primitive functions for simple communication */ |
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1, |
ipcarg_t arg2, ipcarg_t arg3) |
{ |
ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL, !in_interrupt_handler); |
} |
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2) |
{ |
ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL, !in_interrupt_handler); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/libadt/hash_table.c |
---|
0,0 → 1,179 |
/* |
* 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 |
*/ |
/* |
* This is an implementation of generic chained hash table. |
*/ |
#include <libadt/hash_table.h> |
#include <libadt/list.h> |
#include <unistd.h> |
#include <malloc.h> |
#include <assert.h> |
#include <stdio.h> |
#include <string.h> |
/** Create chained hash table. |
* |
* @param h Hash table structure. Will be initialized by this call. |
* @param m Number of slots in the hash table. |
* @param max_keys Maximal number of keys needed to identify an item. |
* @param op Hash table operations structure. |
* @return true on success |
*/ |
int hash_table_create(hash_table_t *h, hash_count_t m, hash_count_t max_keys, hash_table_operations_t *op) |
{ |
hash_count_t i; |
assert(h); |
assert(op && op->hash && op->compare); |
assert(max_keys > 0); |
h->entry = malloc(m * sizeof(link_t)); |
if (!h->entry) { |
printf("cannot allocate memory for hash table\n"); |
return false; |
} |
memset((void *) h->entry, 0, m * sizeof(link_t)); |
for (i = 0; i < m; i++) |
list_initialize(&h->entry[i]); |
h->entries = m; |
h->max_keys = max_keys; |
h->op = op; |
return true; |
} |
/** Insert item into hash table. |
* |
* @param h Hash table. |
* @param key Array of all keys necessary to compute hash index. |
* @param item Item to be inserted into the hash table. |
*/ |
void hash_table_insert(hash_table_t *h, unsigned long key[], link_t *item) |
{ |
hash_index_t chain; |
assert(item); |
assert(h && h->op && h->op->hash && h->op->compare); |
chain = h->op->hash(key); |
assert(chain < h->entries); |
list_append(item, &h->entry[chain]); |
} |
/** Search hash table for an item matching keys. |
* |
* @param h Hash table. |
* @param key Array of all keys needed to compute hash index. |
* |
* @return Matching item on success, NULL if there is no such item. |
*/ |
link_t *hash_table_find(hash_table_t *h, unsigned long key[]) |
{ |
link_t *cur; |
hash_index_t chain; |
assert(h && h->op && h->op->hash && h->op->compare); |
chain = h->op->hash(key); |
assert(chain < h->entries); |
for (cur = h->entry[chain].next; cur != &h->entry[chain]; cur = cur->next) { |
if (h->op->compare(key, h->max_keys, cur)) { |
/* |
* The entry is there. |
*/ |
return cur; |
} |
} |
return NULL; |
} |
/** Remove all matching items from hash table. |
* |
* For each removed item, h->remove_callback() is called. |
* |
* @param h Hash table. |
* @param key Array of keys that will be compared against items of the hash table. |
* @param keys Number of keys in the 'key' array. |
*/ |
void hash_table_remove(hash_table_t *h, unsigned long key[], hash_count_t keys) |
{ |
hash_index_t chain; |
link_t *cur; |
assert(h && h->op && h->op->hash && h->op->compare && h->op->remove_callback); |
assert(keys <= h->max_keys); |
if (keys == h->max_keys) { |
/* |
* All keys are known, hash_table_find() can be used to find the entry. |
*/ |
cur = hash_table_find(h, key); |
if (cur) { |
list_remove(cur); |
h->op->remove_callback(cur); |
} |
return; |
} |
/* |
* Fewer keys were passed. |
* Any partially matching entries are to be removed. |
*/ |
for (chain = 0; chain < h->entries; chain++) { |
for (cur = h->entry[chain].next; cur != &h->entry[chain]; cur = cur->next) { |
if (h->op->compare(key, keys, cur)) { |
link_t *hlp; |
hlp = cur; |
cur = cur->prev; |
list_remove(hlp); |
h->op->remove_callback(hlp); |
continue; |
} |
} |
} |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/libadt/list.c |
---|
0,0 → 1,89 |
/* |
* Copyright (c) 2004 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 <libadt/list.h> |
/** Check for membership |
* |
* Check whether link is contained in the list head. |
* The membership is defined as pointer equivalence. |
* |
* @param link Item to look for. |
* @param head List to look in. |
* |
* @return true if link is contained in head, false otherwise. |
* |
*/ |
int list_member(const link_t *link, const link_t *head) |
{ |
int found = false; |
link_t *hlp = head->next; |
while (hlp != head) { |
if (hlp == link) { |
found = true; |
break; |
} |
hlp = hlp->next; |
} |
return found; |
} |
/** Concatenate two lists |
* |
* Concatenate lists head1 and head2, producing a single |
* list head1 containing items from both (in head1, head2 |
* order) and empty list head2. |
* |
* @param head1 First list and concatenated output |
* @param head2 Second list and empty output. |
* |
*/ |
void list_concat(link_t *head1, link_t *head2) |
{ |
if (list_empty(head2)) |
return; |
head2->next->prev = head1->prev; |
head2->prev->next = head1; |
head1->prev->next = head2->next; |
head1->prev = head2->prev; |
list_initialize(head2); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/stdlib.c |
---|
0,0 → 1,50 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 <stdlib.h> |
static long glbl_seed = 1; |
long int random(void) |
{ |
return glbl_seed = ((1366*glbl_seed + 150889) % RAND_MAX); |
} |
void srandom(unsigned int seed) |
{ |
glbl_seed = seed; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/printf.c |
---|
0,0 → 1,60 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <io/printf_core.h> |
#include <stdio.h> |
#include <stdio.h> |
/** Print formatted text. |
* @param fmt format string |
* \see For more details about format string see printf_core. |
*/ |
int printf(const char *fmt, ...) |
{ |
int ret; |
va_list args; |
va_start(args, fmt); |
ret = vprintf(fmt, args); |
va_end(args); |
return ret; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/snprintf.c |
---|
0,0 → 1,59 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <stdarg.h> |
#include <stdio.h> |
#include <io/printf_core.h> |
/** Print formatted to the given buffer with limited size. |
* @param str buffer |
* @param size buffer size |
* @param fmt format string |
* \see For more details about format string see printf_core. |
*/ |
int snprintf(char *str, size_t size, const char *fmt, ...) |
{ |
int ret; |
va_list args; |
va_start(args, fmt); |
ret = vsnprintf(str, size, fmt, args); |
va_end(args); |
return ret; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/sprintf.c |
---|
0,0 → 1,58 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <stdarg.h> |
#include <stdio.h> |
#include <io/printf_core.h> |
/** Print formatted to the given buffer. |
* @param str buffer |
* @param fmt format string |
* \see For more details about format string see printf_core. |
*/ |
int sprintf(char *str, const char *fmt, ...) |
{ |
int ret; |
va_list args; |
va_start(args, fmt); |
ret = vsprintf(str, fmt, args); |
va_end(args); |
return ret; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/io.c |
---|
0,0 → 1,113 |
/* |
* Copyright (c) 2005 Martin Decky |
* 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 <unistd.h> |
#include <stdio.h> |
#include <io/io.h> |
static char nl = '\n'; |
int puts(const char * str) |
{ |
size_t count; |
if (str == NULL) { |
return putnchars("(NULL)",6 ); |
} |
for (count = 0; str[count] != 0; count++); |
if (write(1, (void * ) str, count) == count) { |
if (write(1, &nl, 1) == 1) |
return 0; |
} |
return EOF; |
} |
/** Put count chars from buffer to stdout without adding newline |
* @param buf Buffer with size at least count bytes - NULL pointer NOT allowed! |
* @param count |
* @return 0 on succes, EOF on fail |
*/ |
int putnchars(const char * buf, size_t count) |
{ |
if (write(1, (void * ) buf, count) == count) { |
return 0; |
} |
return EOF; |
} |
/** Same as puts, but does not print newline at end |
* |
*/ |
int putstr(const char * str) |
{ |
size_t count; |
if (str == NULL) { |
return putnchars("(NULL)",6 ); |
} |
for (count = 0; str[count] != 0; count++); |
if (write(1, (void * ) str, count) == count) { |
return 0; |
} |
return EOF; |
} |
int putchar(int c) |
{ |
unsigned char ch = c; |
if (write(1, (void *)&ch , 1) == 1) { |
return c; |
} |
return EOF; |
} |
int getchar(void) |
{ |
unsigned char c; |
if (read(0, (void *)&c , 1) == 1) { |
return c; |
} |
return EOF; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/vprintf.c |
---|
0,0 → 1,60 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <stdarg.h> |
#include <stdio.h> |
#include <unistd.h> |
#include <io/printf_core.h> |
int vprintf_write(const char *str, size_t count, void *unused); |
int vprintf_write(const char *str, size_t count, void *unused) |
{ |
return write(1, str, count); |
} |
/** Print formatted text. |
* @param fmt format string |
* @param ap format parameters |
* \see For more details about format string see printf_core. |
*/ |
int vprintf(const char *fmt, va_list ap) |
{ |
struct printf_spec ps = {(int(*)(void *, size_t, void *))vprintf_write, NULL}; |
return printf_core(fmt, &ps, ap); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/vsnprintf.c |
---|
0,0 → 1,111 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <stdarg.h> |
#include <stdio.h> |
#include <string.h> |
#include <io/printf_core.h> |
struct vsnprintf_data { |
size_t size; /* total space for string */ |
size_t len; /* count of currently used characters */ |
char *string; /* destination string */ |
}; |
int vsnprintf_write(const char *str, size_t count, struct vsnprintf_data *data); |
/** Write string to given buffer. |
* Write at most data->size characters including trailing zero. According to C99 has snprintf to return number |
* of characters that would have been written if enough space had been available. Hence the return value is not |
* number of really printed characters but size of input string. Number of really used characters |
* is stored in data->len. |
* @param str source string to print |
* @param count size of source string |
* @param data structure with destination string, counter of used space and total string size. |
* @return number of characters to print (not characters really printed!) |
*/ |
int vsnprintf_write(const char *str, size_t count, struct vsnprintf_data *data) |
{ |
size_t i; |
i = data->size - data->len; |
if (i == 0) { |
return count; |
} |
if (i == 1) { |
/* We have only one free byte left in buffer => write there trailing zero */ |
data->string[data->size - 1] = 0; |
data->len = data->size; |
return count; |
} |
if (i <= count) { |
/* We have not enought space for whole string with the trailing zero => print only a part of string */ |
memcpy((void *)(data->string + data->len), (void *)str, i - 1); |
data->string[data->size - 1] = 0; |
data->len = data->size; |
return count; |
} |
/* Buffer is big enought to print whole string */ |
memcpy((void *)(data->string + data->len), (void *)str, count); |
data->len += count; |
/* Put trailing zero at end, but not count it into data->len so it could be rewritten next time */ |
data->string[data->len] = 0; |
return count; |
} |
/** Print formatted to the given buffer with limited size. |
* @param str buffer |
* @param size buffer size |
* @param fmt format string |
* \see For more details about format string see printf_core. |
*/ |
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) |
{ |
struct vsnprintf_data data = {size, 0, str}; |
struct printf_spec ps = {(int(*)(void *, size_t, void *))vsnprintf_write, &data}; |
/* Print 0 at end of string - fix the case that nothing will be printed */ |
if (size > 0) |
str[0] = 0; |
/* vsnprintf_write ensures that str will be terminated by zero. */ |
return printf_core(fmt, &ps, ap); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/printf_core.c |
---|
0,0 → 1,692 |
/* |
* Copyright (c) 2001-2004 Jakub Jermar |
* Copyright (c) 2006 Josef Cejka |
* 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 |
* @brief Printing functions. |
*/ |
#include <unistd.h> |
#include <stdio.h> |
#include <io/printf_core.h> |
#include <ctype.h> |
#include <string.h> |
/* For serialization */ |
#include <async.h> |
#define __PRINTF_FLAG_PREFIX 0x00000001 /**< show prefixes 0x or 0*/ |
#define __PRINTF_FLAG_SIGNED 0x00000002 /**< signed / unsigned number */ |
#define __PRINTF_FLAG_ZEROPADDED 0x00000004 /**< print leading zeroes */ |
#define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /**< align to left */ |
#define __PRINTF_FLAG_SHOWPLUS 0x00000020 /**< always show + sign */ |
#define __PRINTF_FLAG_SPACESIGN 0x00000040 /**< print space instead of plus */ |
#define __PRINTF_FLAG_BIGCHARS 0x00000080 /**< show big characters */ |
#define __PRINTF_FLAG_NEGATIVE 0x00000100 /**< number has - sign */ |
#define PRINT_NUMBER_BUFFER_SIZE (64+5) /**< Buffer big enought for 64 bit number |
* printed in base 2, sign, prefix and |
* 0 to terminate string.. (last one is only for better testing |
* end of buffer by zero-filling subroutine) |
*/ |
/** Enumeration of possible arguments types. |
*/ |
typedef enum { |
PrintfQualifierByte = 0, |
PrintfQualifierShort, |
PrintfQualifierInt, |
PrintfQualifierLong, |
PrintfQualifierLongLong, |
PrintfQualifierSizeT, |
PrintfQualifierPointer |
} qualifier_t; |
static char digits_small[] = "0123456789abcdef"; /**< Small hexadecimal characters */ |
static char digits_big[] = "0123456789ABCDEF"; /**< Big hexadecimal characters */ |
/** Print count chars from buffer without adding newline |
* @param buf Buffer with size at least count bytes - NULL pointer NOT allowed! |
* @param count |
* @param ps output method and its data |
* @return number of printed characters |
*/ |
static int printf_putnchars(const char * buf, size_t count, struct printf_spec *ps) |
{ |
return ps->write((void *)buf, count, ps->data); |
} |
/** Print string without added newline |
* @param str string to print |
* @param ps write function specification and support data |
* @return number of printed characters |
*/ |
static int printf_putstr(const char * str, struct printf_spec *ps) |
{ |
size_t count; |
if (str == NULL) { |
return printf_putnchars("(NULL)", 6, ps); |
} |
for (count = 0; str[count] != 0; count++); |
if (ps->write((void *) str, count, ps->data) == count) { |
return 0; |
} |
return EOF; |
} |
/** Print one character to output |
* @param c one character |
* @param ps output method |
* @return number of printed characters |
*/ |
static int printf_putchar(int c, struct printf_spec *ps) |
{ |
unsigned char ch = c; |
return ps->write((void *) &ch, 1, ps->data); |
} |
/** Print one formatted character |
* @param c character to print |
* @param width |
* @param flags |
* @return number of printed characters |
*/ |
static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps) |
{ |
int counter = 0; |
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) { |
while (--width > 0) { /* one space is consumed by character itself hence predecrement */ |
if (printf_putchar(' ', ps) > 0) |
++counter; |
} |
} |
if (printf_putchar(c, ps) > 0) |
counter++; |
while (--width > 0) { /* one space is consumed by character itself hence predecrement */ |
if (printf_putchar(' ', ps) > 0) |
++counter; |
} |
return ++counter; |
} |
/** Print one string |
* @param s string |
* @param width |
* @param precision |
* @param flags |
* @return number of printed characters |
*/ |
static int print_string(char *s, int width, int precision, uint64_t flags, struct printf_spec *ps) |
{ |
int counter = 0; |
size_t size; |
int retval; |
if (s == NULL) { |
return printf_putstr("(NULL)", ps); |
} |
size = strlen(s); |
/* print leading spaces */ |
if (precision == 0) |
precision = size; |
width -= precision; |
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) { |
while (width-- > 0) { |
if (printf_putchar(' ', ps) == 1) |
counter++; |
} |
} |
while (precision > size) { |
precision--; |
if (printf_putchar(' ', ps) == 1) |
++counter; |
} |
if ((retval = printf_putnchars(s, precision, ps)) < 0) { |
return -counter; |
} |
counter += retval; |
while (width-- > 0) { |
if (printf_putchar(' ', ps) == 1) |
++counter; |
} |
return counter; |
} |
/** Print number in given base |
* |
* Print significant digits of a number in given |
* base. |
* |
* @param num Number to print. |
* @param width |
* @param precision |
* @param base Base to print the number in (should |
* be in range 2 .. 16). |
* @param flags output modifiers |
* @return number of printed characters |
* |
*/ |
static int print_number(uint64_t num, int width, int precision, int base , uint64_t flags, struct printf_spec *ps) |
{ |
char *digits = digits_small; |
char d[PRINT_NUMBER_BUFFER_SIZE]; /* this is good enough even for base == 2, prefix and sign */ |
char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1]; |
int size = 0; /* size of number with all prefixes and signs */ |
int number_size; /* size of plain number */ |
char sgn; |
int retval; |
int counter = 0; |
if (flags & __PRINTF_FLAG_BIGCHARS) |
digits = digits_big; |
*ptr-- = 0; /* Put zero at end of string */ |
if (num == 0) { |
*ptr-- = '0'; |
size++; |
} else { |
do { |
*ptr-- = digits[num % base]; |
size++; |
} while (num /= base); |
} |
number_size = size; |
/* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */ |
if (flags & __PRINTF_FLAG_PREFIX) { |
switch(base) { |
case 2: /* Binary formating is not standard, but usefull */ |
size += 2; |
break; |
case 8: |
size++; |
break; |
case 16: |
size += 2; |
break; |
} |
} |
sgn = 0; |
if (flags & __PRINTF_FLAG_SIGNED) { |
if (flags & __PRINTF_FLAG_NEGATIVE) { |
sgn = '-'; |
size++; |
} else if (flags & __PRINTF_FLAG_SHOWPLUS) { |
sgn = '+'; |
size++; |
} else if (flags & __PRINTF_FLAG_SPACESIGN) { |
sgn = ' '; |
size++; |
} |
} |
if (flags & __PRINTF_FLAG_LEFTALIGNED) { |
flags &= ~__PRINTF_FLAG_ZEROPADDED; |
} |
/* if number is leftaligned or precision is specified then zeropadding is ignored */ |
if (flags & __PRINTF_FLAG_ZEROPADDED) { |
if ((precision == 0) && (width > size)) { |
precision = width - size + number_size; |
} |
} |
/* print leading spaces */ |
if (number_size > precision) /* We must print whole number not only a part */ |
precision = number_size; |
width -= precision + size - number_size; |
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) { |
while (width-- > 0) { |
if (printf_putchar(' ', ps) == 1) |
counter++; |
} |
} |
/* print sign */ |
if (sgn) { |
if (printf_putchar(sgn, ps) == 1) |
counter++; |
} |
/* print prefix */ |
if (flags & __PRINTF_FLAG_PREFIX) { |
switch(base) { |
case 2: /* Binary formating is not standard, but usefull */ |
if (printf_putchar('0', ps) == 1) |
counter++; |
if (flags & __PRINTF_FLAG_BIGCHARS) { |
if (printf_putchar('B', ps) == 1) |
counter++; |
} else { |
if (printf_putchar('b', ps) == 1) |
counter++; |
} |
break; |
case 8: |
if (printf_putchar('o', ps) == 1) |
counter++; |
break; |
case 16: |
if (printf_putchar('0', ps) == 1) |
counter++; |
if (flags & __PRINTF_FLAG_BIGCHARS) { |
if (printf_putchar('X', ps) == 1) |
counter++; |
} else { |
if (printf_putchar('x', ps) == 1) |
counter++; |
} |
break; |
} |
} |
/* print leading zeroes */ |
precision -= number_size; |
while (precision-- > 0) { |
if (printf_putchar('0', ps) == 1) |
counter++; |
} |
/* print number itself */ |
if ((retval = printf_putstr(++ptr, ps)) > 0) { |
counter += retval; |
} |
/* print ending spaces */ |
while (width-- > 0) { |
if (printf_putchar(' ', ps) == 1) |
counter++; |
} |
return counter; |
} |
/** Print formatted string. |
* |
* Print string formatted according to the fmt parameter |
* and variadic arguments. Each formatting directive |
* must have the following form: |
* |
* \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION |
* |
* FLAGS:@n |
* - "#" Force to print prefix. |
* For conversion \%o the prefix is 0, for %x and \%X prefixes are 0x and 0X |
* and for conversion \%b the prefix is 0b. |
* |
* - "-" Align to left. |
* |
* - "+" Print positive sign just as negative. |
* |
* - " " If the printed number is positive and "+" flag is not set, print space in |
* place of sign. |
* |
* - "0" Print 0 as padding instead of spaces. Zeroes are placed between sign and the |
* rest of the number. This flag is ignored if "-" flag is specified. |
* |
* WIDTH:@n |
* - Specify minimal width of printed argument. If it is bigger, width is ignored. |
* If width is specified with a "*" character instead of number, width is taken from |
* parameter list. And integer parameter is expected before parameter for processed |
* conversion specification. If this value is negative its absolute value is taken |
* and the "-" flag is set. |
* |
* PRECISION:@n |
* - Value precision. For numbers it specifies minimum valid numbers. |
* Smaller numbers are printed with leading zeroes. Bigger numbers are not affected. |
* Strings with more than precision characters are cut off. |
* Just as with width, an "*" can be used used instead of a number. |
* An integer value is then expected in parameters. When both width and precision |
* are specified using "*", the first parameter is used for width and the second one |
* for precision. |
* |
* TYPE:@n |
* - "hh" Signed or unsigned char.@n |
* - "h" Signed or usigned short.@n |
* - "" Signed or usigned int (default value).@n |
* - "l" Signed or usigned long int.@n |
* - "ll" Signed or usigned long long int.@n |
* - "z" Type size_t.@n |
* |
* |
* CONVERSION:@n |
* - % Print percentile character itself. |
* |
* - c Print single character. |
* |
* - s Print zero terminated string. If a NULL value is passed as value, "(NULL)" is printed instead. |
* |
* - P, p Print value of a pointer. Void * value is expected and it is printed in hexadecimal notation with prefix |
* (as with '\%#X' or '\%#x' for 32bit or '\%#X' or '\%#x' for 64bit long pointers). |
* |
* - b Print value as unsigned binary number. Prefix is not printed by default. (Nonstandard extension.) |
* |
* - o Print value as unsigned octal number. Prefix is not printed by default. |
* |
* - d,i Print signed decimal number. There is no difference between d and i conversion. |
* |
* - u Print unsigned decimal number. |
* |
* - X, x Print hexadecimal number with upper- or lower-case. Prefix is not printed by default. |
* |
* All other characters from fmt except the formatting directives |
* are printed in verbatim. |
* |
* @param fmt Formatting NULL terminated string. |
* @return Number of printed characters or negative value on failure. |
*/ |
int printf_core(const char *fmt, struct printf_spec *ps, va_list ap) |
{ |
int i = 0, j = 0; /* i is index of currently processed char from fmt, j is index to the first not printed nonformating character */ |
int end; |
int counter; /* counter of printed characters */ |
int retval; /* used to store return values from called functions */ |
char c; |
qualifier_t qualifier; /* type of argument */ |
int base; /* base in which will be parameter (numbers only) printed */ |
uint64_t number; /* argument value */ |
size_t size; /* byte size of integer parameter */ |
int width, precision; |
uint64_t flags; |
/* Don't let other threads interfere */ |
async_serialize_start(); |
counter = 0; |
while ((c = fmt[i])) { |
/* control character */ |
if (c == '%' ) { |
/* print common characters if any processed */ |
if (i > j) { |
if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */ |
goto minus_out; |
} |
counter += retval; |
} |
j = i; |
/* parse modifiers */ |
flags = 0; |
end = 0; |
do { |
++i; |
switch (c = fmt[i]) { |
case '#': flags |= __PRINTF_FLAG_PREFIX; break; |
case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break; |
case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break; |
case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break; |
case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break; |
default: end = 1; |
}; |
} while (end == 0); |
/* width & '*' operator */ |
width = 0; |
if (isdigit(fmt[i])) { |
while (isdigit(fmt[i])) { |
width *= 10; |
width += fmt[i++] - '0'; |
} |
} else if (fmt[i] == '*') { |
/* get width value from argument list*/ |
i++; |
width = (int)va_arg(ap, int); |
if (width < 0) { |
/* negative width means to set '-' flag */ |
width *= -1; |
flags |= __PRINTF_FLAG_LEFTALIGNED; |
} |
} |
/* precision and '*' operator */ |
precision = 0; |
if (fmt[i] == '.') { |
++i; |
if (isdigit(fmt[i])) { |
while (isdigit(fmt[i])) { |
precision *= 10; |
precision += fmt[i++] - '0'; |
} |
} else if (fmt[i] == '*') { |
/* get precision value from argument list*/ |
i++; |
precision = (int)va_arg(ap, int); |
if (precision < 0) { |
/* negative precision means to ignore it */ |
precision = 0; |
} |
} |
} |
switch (fmt[i++]) { |
/** TODO: unimplemented qualifiers: |
* t ptrdiff_t - ISO C 99 |
*/ |
case 'h': /* char or short */ |
qualifier = PrintfQualifierShort; |
if (fmt[i] == 'h') { |
i++; |
qualifier = PrintfQualifierByte; |
} |
break; |
case 'l': /* long or long long*/ |
qualifier = PrintfQualifierLong; |
if (fmt[i] == 'l') { |
i++; |
qualifier = PrintfQualifierLongLong; |
} |
break; |
case 'z': /* size_t */ |
qualifier = PrintfQualifierSizeT; |
break; |
default: |
qualifier = PrintfQualifierInt; /* default type */ |
--i; |
} |
base = 10; |
switch (c = fmt[i]) { |
/* |
* String and character conversions. |
*/ |
case 's': |
if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) { |
goto minus_out; |
} |
counter += retval; |
j = i + 1; |
goto next_char; |
case 'c': |
c = va_arg(ap, unsigned int); |
if ((retval = print_char(c, width, flags, ps)) < 0) { |
goto minus_out; |
} |
counter += retval; |
j = i + 1; |
goto next_char; |
/* |
* Integer values |
*/ |
case 'P': /* pointer */ |
flags |= __PRINTF_FLAG_BIGCHARS; |
case 'p': |
flags |= __PRINTF_FLAG_PREFIX; |
base = 16; |
qualifier = PrintfQualifierPointer; |
break; |
case 'b': |
base = 2; |
break; |
case 'o': |
base = 8; |
break; |
case 'd': |
case 'i': |
flags |= __PRINTF_FLAG_SIGNED; |
case 'u': |
break; |
case 'X': |
flags |= __PRINTF_FLAG_BIGCHARS; |
case 'x': |
base = 16; |
break; |
/* percentile itself */ |
case '%': |
j = i; |
goto next_char; |
/* |
* Bad formatting. |
*/ |
default: |
/* Unknown format |
* now, the j is index of '%' so we will |
* print whole bad format sequence |
*/ |
goto next_char; |
} |
/* Print integers */ |
/* print number */ |
switch (qualifier) { |
case PrintfQualifierByte: |
size = sizeof(unsigned char); |
number = (uint64_t)va_arg(ap, unsigned int); |
break; |
case PrintfQualifierShort: |
size = sizeof(unsigned short); |
number = (uint64_t)va_arg(ap, unsigned int); |
break; |
case PrintfQualifierInt: |
size = sizeof(unsigned int); |
number = (uint64_t)va_arg(ap, unsigned int); |
break; |
case PrintfQualifierLong: |
size = sizeof(unsigned long); |
number = (uint64_t)va_arg(ap, unsigned long); |
break; |
case PrintfQualifierLongLong: |
size = sizeof(unsigned long long); |
number = (uint64_t)va_arg(ap, unsigned long long); |
break; |
case PrintfQualifierPointer: |
size = sizeof(void *); |
number = (uint64_t)(unsigned long)va_arg(ap, void *); |
break; |
case PrintfQualifierSizeT: |
size = sizeof(size_t); |
number = (uint64_t)va_arg(ap, size_t); |
break; |
default: /* Unknown qualifier */ |
goto minus_out; |
} |
if (flags & __PRINTF_FLAG_SIGNED) { |
if (number & (0x1 << (size*8 - 1))) { |
flags |= __PRINTF_FLAG_NEGATIVE; |
if (size == sizeof(uint64_t)) { |
number = -((int64_t)number); |
} else { |
number = ~number; |
number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8))); |
number++; |
} |
} |
} |
if ((retval = print_number(number, width, precision, base, flags, ps)) < 0 ) { |
goto minus_out; |
}; |
counter += retval; |
j = i + 1; |
} |
next_char: |
++i; |
} |
if (i > j) { |
if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */ |
goto minus_out; |
} |
counter += retval; |
} |
async_serialize_end(); |
return counter; |
minus_out: |
async_serialize_end(); |
return -counter; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/stream.c |
---|
0,0 → 1,189 |
/* |
* Copyright (c) 2006 Josef Cejka |
* Copyright (c) 2006 Jakub Vana |
* 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 <io/io.h> |
#include <io/stream.h> |
#include <string.h> |
#include <malloc.h> |
#include <libc.h> |
#include <ipc/ipc.h> |
#include <ipc/ns.h> |
#include <ipc/fb.h> |
#include <ipc/services.h> |
#include <console.h> |
#include <unistd.h> |
#include <async.h> |
#define FDS 32 |
typedef struct stream_t { |
pwritefn_t w; |
preadfn_t r; |
void * param; |
int phone; |
} stream_t; |
static int console_phone = -1; |
static stream_t streams[FDS]; |
static ssize_t write_stderr(void *param, const void *buf, size_t count) |
{ |
return count; |
} |
static ssize_t read_stdin(void *param, void *buf, size_t count) |
{ |
ipcarg_t r0,r1; |
size_t i = 0; |
while (i < count) { |
if (async_req_2(streams[0].phone, CONSOLE_GETCHAR, 0, 0, &r0, &r1) < 0) { |
return -1; |
} |
((char *) buf)[i++] = r0; |
} |
return i; |
} |
static ssize_t write_stdout(void *param, const void *buf, size_t count) |
{ |
int i; |
for (i = 0; i < count; i++) |
async_msg(streams[1].phone, CONSOLE_PUTCHAR, ((const char *) buf)[i]); |
return count; |
} |
static stream_t open_stdin(void) |
{ |
stream_t stream; |
if (console_phone < 0) { |
while ((console_phone = ipc_connect_me_to(PHONE_NS, SERVICE_CONSOLE, 0)) < 0) { |
usleep(10000); |
} |
} |
stream.r = read_stdin; |
stream.w = NULL; |
stream.param = 0; |
stream.phone = console_phone; |
return stream; |
} |
static stream_t open_stdout(void) |
{ |
stream_t stream; |
if (console_phone < 0) { |
while ((console_phone = ipc_connect_me_to(PHONE_NS, SERVICE_CONSOLE, 0)) < 0) { |
usleep(10000); |
} |
} |
stream.r = NULL; |
stream.w = write_stdout; |
stream.phone = console_phone; |
stream.param = 0; |
return stream; |
} |
static ssize_t write_null(void *param, const void *buf, size_t count) |
{ |
return count; |
} |
fd_t open(const char *fname, int flags) |
{ |
int c = 0; |
while (((streams[c].w) || (streams[c].r)) && (c < FDS)) |
c++; |
if (c == FDS) |
return EMFILE; |
if (!strcmp(fname, "stdin")) { |
streams[c] = open_stdin(); |
return c; |
} |
if (!strcmp(fname, "stdout")) { |
streams[c] = open_stdout(); |
return c; |
} |
if (!strcmp(fname, "stderr")) { |
streams[c].w = write_stderr; |
return c; |
} |
if (!strcmp(fname, "null")) { |
streams[c].w = write_null; |
return c; |
} |
return -1; |
} |
ssize_t write(int fd, const void *buf, size_t count) |
{ |
if (fd < FDS && streams[fd].w) |
return streams[fd].w(streams[fd].param, buf, count); |
return 0; |
} |
ssize_t read(int fd, void *buf, size_t count) |
{ |
if (fd < FDS && streams[fd].r) |
return streams[fd].r(streams[fd].param, buf, count); |
return 0; |
} |
int get_fd_phone(int fd) |
{ |
if (fd >= FDS || fd < 0) |
return -1; |
return streams[fd].phone; |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/io/vsprintf.c |
---|
0,0 → 1,51 |
/* |
* Copyright (c) 2006 Josef Cejka |
* 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 <stdarg.h> |
#include <stdio.h> |
#include <io/printf_core.h> |
/** Print formatted to the given buffer. |
* @param str buffer |
* @param fmt format string |
* @param ap argument list |
* \see For more details about format string see printf_core. |
*/ |
int vsprintf(char *str, const char *fmt, va_list ap) |
{ |
return vsnprintf(str, (size_t)-1, fmt, ap); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/mman.c |
---|
0,0 → 1,58 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 <sys/mman.h> |
#include <as.h> |
#include <unistd.h> |
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) |
{ |
if (!start) |
start = as_get_mappable_page(length, 0); |
// if (! ((flags & MAP_SHARED) ^ (flags & MAP_PRIVATE))) |
// return MAP_FAILED; |
if (! (flags & MAP_ANONYMOUS)) |
return MAP_FAILED; |
return as_area_create(start, length, prot); |
} |
int munmap(void *start, size_t length) |
{ |
return as_area_destroy(start); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/err.c |
---|
0,0 → 1,46 |
/* |
* Copyright (c) 2006 Ondrej Palkovsky |
* 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 <stdio.h> |
#include <stdlib.h> |
/* TODO |
void errx(int __status, __const char *__format, ...) |
{ |
_exit(0); |
} |
*/ |
/** @} |
*/ |
/trunk/uspace/libc/generic/sysinfo.c |
---|
0,0 → 1,45 |
/* |
* 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 <sysinfo.h> |
#include <string.h> |
sysarg_t sysinfo_value(char *name) |
{ |
return __SYSCALL2(SYS_SYSINFO_VALUE, (sysarg_t ) name, (sysarg_t) strlen(name)); |
} |
/** @} |
*/ |
/trunk/uspace/libc/generic/futex.c |
---|
0,0 → 1,177 |
/* |
* 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 <futex.h> |
#include <atomic.h> |
#include <libc.h> |
#include <stdio.h> |
#include <types.h> |
#include <kernel/synch/synch.h> |
/* |
* Note about race conditions. |
* Because of non-atomic nature of operations performed sequentially on the futex |
* counter and the futex wait queue, there is a race condition: |
* |
* (wq->missed_wakeups == 1) && (futex->count = 1) |
* |
* Scenario 1 (wait queue timeout vs. futex_up()): |
* 1. assume wq->missed_wakeups == 0 && futex->count == -1 |
* (ie. thread A sleeping, thread B in the critical section) |
* 2. A receives timeout and gets removed from the wait queue |
* 3. B wants to leave the critical section and calls futex_up() |
* 4. B thus changes futex->count from -1 to 0 |
* 5. B has to call SYS_FUTEX_WAKEUP syscall to wake up the sleeping thread |
* 6. B finds the wait queue empty and changes wq->missed_wakeups from 0 to 1 |
* 7. A fixes futex->count (i.e. the number of waiting threads) by changing it from 0 to 1 |
* |
* Scenario 2 (conditional down operation vs. futex_up) |
* 1. assume wq->missed_wakeups == 0 && futex->count == 0 |
* (i.e. thread A is in the critical section) |
* 2. thread B performs futex_trydown() operation and changes futex->count from 0 to -1 |
* B is now obliged to call SYS_FUTEX_SLEEP syscall |
* 3. A wants to leave the critical section and does futex_up() |
* 4. A thus changes futex->count from -1 to 0 and must call SYS_FUTEX_WAKEUP syscall |
* 5. B finds the wait queue empty and immediatelly aborts the conditional sleep |
* 6. No thread is queueing in the wait queue so wq->missed_wakeups changes from 0 to 1 |
* 6. B fixes futex->count (i.e. the number of waiting threads) by changing it from 0 to 1 |
* |
* Both scenarios allow two threads to be in the critical section simultaneously. |
* One without kernel intervention and the other through wq->missed_wakeups being 1. |
* |
* To mitigate this problem, futex_down_timeout() detects that the syscall didn't sleep |
* in the wait queue, fixes the futex counter and RETRIES the whole operation again. |
* |
*/ |
/** Initialize futex counter. |
* |
* @param futex Futex. |
* @param val Initialization value. |
*/ |
void futex_initialize(atomic_t *futex, int val) |
{ |
atomic_set(futex, val); |
} |
int futex_down(atomic_t *futex) |
{ |
return futex_down_timeout(futex, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE); |
} |
int futex_trydown(atomic_t *futex) |
{ |
return futex_down_timeout(futex, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING); |
} |
/** Try to down the futex. |
* |
* @param futex Futex. |
* @param usec Microseconds to wait. Zero value means sleep without timeout. |
* @param flags Select mode of operation. See comment for waitq_sleep_timeout(). |
* |
* @return ENOENT if there is no such virtual address. One of ESYNCH_OK_ATOMIC |
* and ESYNCH_OK_BLOCKED on success or ESYNCH_TIMEOUT if the lock was |
* not acquired because of a timeout or ESYNCH_WOULD_BLOCK if the |
* operation could not be carried out atomically (if requested so). |
*/ |
int futex_down_timeout(atomic_t *futex, uint32_t usec, int flags) |
{ |
int rc; |
while (atomic_predec(futex) < 0) { |
rc = __SYSCALL3(SYS_FUTEX_SLEEP, (sysarg_t) &futex->count, (sysarg_t) usec, (sysarg_t) flags); |
switch (rc) { |
case ESYNCH_OK_ATOMIC: |
/* |
* Because of a race condition between timeout and futex_up() |
* and between conditional futex_down_timeout() and futex_up(), |
* we have to give up and try again in this special case. |
*/ |
atomic_inc(futex); |
break; |
case ESYNCH_TIMEOUT: |
atomic_inc(futex); |
return ESYNCH_TIMEOUT; |
break; |
case ESYNCH_WOULD_BLOCK: |
/* |
* The conditional down operation should be implemented this way. |
* The userspace-only variant tends to accumulate missed wakeups |
* in the kernel futex wait queue. |
*/ |
atomic_inc(futex); |
return ESYNCH_WOULD_BLOCK; |
break; |
case ESYNCH_OK_BLOCKED: |
/* |
* Enter the critical section. |
* The futex counter has already been incremented for us. |
*/ |
return ESYNCH_OK_BLOCKED; |
break; |
default: |
return rc; |
} |
} |
/* |
* Enter the critical section. |
*/ |
return ESYNCH_OK_ATOMIC; |
} |
/** Up the futex. |
* |
* @param futex Futex. |
* |
* @return ENOENT if there is no such virtual address. Otherwise zero. |
*/ |
int futex_up(atomic_t *futex) |
{ |
long val; |
val = atomic_postinc(futex); |
if (val < 0) |
return __SYSCALL1(SYS_FUTEX_WAKEUP, (sysarg_t) &futex->count); |
return 0; |
} |
/** @} |
*/ |