blob: d6a1df22f3a97570619adacf032c879413eb0eea [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/os.h>
#include <pj/assert.h>
#include <pj/pool.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h>
#include <pj/guid.h>
#include <pj/except.h>
#include <pj/errno.h>
#include "os_symbian.h"
#define PJ_MAX_TLS 32
#define DUMMY_MUTEX ((pj_mutex_t*)101)
#define DUMMY_SEMAPHORE ((pj_sem_t*)102)
#define THIS_FILE "os_core_symbian.c"
/* Default message slot number for RSocketServ::Connect().
* Increase it to 32 from the default 8 (KESockDefaultMessageSlots)
*/
#ifndef PJ_SYMBIAN_SOCK_MSG_SLOTS
# define PJ_SYMBIAN_SOCK_MSG_SLOTS 32
#endif
/*
* Note:
*
* The Symbian implementation does not support threading!
*/
struct pj_thread_t
{
char obj_name[PJ_MAX_OBJ_NAME];
void *tls_values[PJ_MAX_TLS];
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
pj_uint32_t stk_size;
pj_uint32_t stk_max_usage;
char *stk_start;
const char *caller_file;
int caller_line;
#endif
} main_thread;
struct pj_atomic_t
{
pj_atomic_value_t value;
};
struct pj_sem_t
{
int value;
int max;
};
/* Flag and reference counter for PJLIB instance */
static int initialized;
/* Flags to indicate which TLS variables have been used */
static int tls_vars[PJ_MAX_TLS];
/* atexit handlers */
static unsigned atexit_count;
static void (*atexit_func[32])(void);
/////////////////////////////////////////////////////////////////////////////
//
// CPjTimeoutTimer implementation
//
CPjTimeoutTimer::CPjTimeoutTimer()
: CActive(PJ_SYMBIAN_TIMER_PRIORITY), hasTimedOut_(PJ_FALSE)
{
}
CPjTimeoutTimer::~CPjTimeoutTimer()
{
Cancel();
timer_.Close();
}
void CPjTimeoutTimer::ConstructL()
{
hasTimedOut_ = PJ_FALSE;
timer_.CreateLocal();
CActiveScheduler::Add(this);
}
CPjTimeoutTimer *CPjTimeoutTimer::NewL()
{
CPjTimeoutTimer *self = new CPjTimeoutTimer;
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
void CPjTimeoutTimer::StartTimer(TUint miliSeconds)
{
Cancel();
hasTimedOut_ = PJ_FALSE;
timer_.After(iStatus, miliSeconds * 1000);
SetActive();
}
bool CPjTimeoutTimer::HasTimedOut() const
{
return hasTimedOut_ != 0;
}
void CPjTimeoutTimer::RunL()
{
hasTimedOut_ = PJ_TRUE;
}
void CPjTimeoutTimer::DoCancel()
{
timer_.Cancel();
}
TInt CPjTimeoutTimer::RunError(TInt aError)
{
PJ_UNUSED_ARG(aError);
return KErrNone;
}
/////////////////////////////////////////////////////////////////////////////
//
// PjSymbianOS implementation
//
PjSymbianOS::PjSymbianOS()
: isConnectionUp_(false),
isSocketServInitialized_(false), isResolverInitialized_(false),
console_(NULL), selectTimeoutTimer_(NULL),
appSocketServ_(NULL), appConnection_(NULL), appHostResolver_(NULL),
appHostResolver6_(NULL)
{
}
// Set parameters
void PjSymbianOS::SetParameters(pj_symbianos_params *params)
{
appSocketServ_ = (RSocketServ*) params->rsocketserv;
appConnection_ = (RConnection*) params->rconnection;
appHostResolver_ = (RHostResolver*) params->rhostresolver;
appHostResolver6_ = (RHostResolver*) params->rhostresolver6;
}
// Get PjSymbianOS instance
PjSymbianOS *PjSymbianOS::Instance()
{
static PjSymbianOS instance_;
return &instance_;
}
// Initialize
TInt PjSymbianOS::Initialize()
{
TInt err;
selectTimeoutTimer_ = CPjTimeoutTimer::NewL();
#if 0
pj_assert(console_ == NULL);
TRAPD(err, console_ = Console::NewL(_L("PJLIB"),
TSize(KConsFullScreen,KConsFullScreen)));
return err;
#endif
/* Only create RSocketServ if application doesn't specify it
* in the parameters
*/
if (!isSocketServInitialized_ && appSocketServ_ == NULL) {
err = socketServ_.Connect(PJ_SYMBIAN_SOCK_MSG_SLOTS);
if (err != KErrNone)
goto on_error;
isSocketServInitialized_ = true;
}
if (!isResolverInitialized_) {
if (appHostResolver_ == NULL) {
if (Connection())
err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream,
*Connection());
else
err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream);
if (err != KErrNone)
goto on_error;
}
#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
if (appHostResolver6_ == NULL) {
if (Connection())
err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream,
*Connection());
else
err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream);
if (err != KErrNone)
goto on_error;
}
#endif
isResolverInitialized_ = true;
}
isConnectionUp_ = true;
return KErrNone;
on_error:
Shutdown();
return err;
}
// Shutdown
void PjSymbianOS::Shutdown()
{
isConnectionUp_ = false;
if (isResolverInitialized_) {
hostResolver_.Close();
#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
hostResolver6_.Close();
#endif
isResolverInitialized_ = false;
}
if (isSocketServInitialized_) {
socketServ_.Close();
isSocketServInitialized_ = false;
}
delete console_;
console_ = NULL;
delete selectTimeoutTimer_;
selectTimeoutTimer_ = NULL;
appSocketServ_ = NULL;
appConnection_ = NULL;
appHostResolver_ = NULL;
appHostResolver6_ = NULL;
}
// Convert to Unicode
TInt PjSymbianOS::ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign)
{
#if 0
pj_assert(conv_ != NULL);
return conv_->ConvertToUnicode(aUnicode, aForeign, convToUnicodeState_);
#else
return CnvUtfConverter::ConvertToUnicodeFromUtf8(aUnicode, aForeign);
#endif
}
// Convert from Unicode
TInt PjSymbianOS::ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode)
{
#if 0
pj_assert(conv_ != NULL);
return conv_->ConvertFromUnicode(aForeign, aUnicode, convToAnsiState_);
#else
return CnvUtfConverter::ConvertFromUnicodeToUtf8(aForeign, aUnicode);
#endif
}
/////////////////////////////////////////////////////////////////////////////
//
// PJLIB os.h implementation
//
PJ_DEF(pj_uint32_t) pj_getpid(void)
{
return 0;
}
/* Set Symbian specific parameters */
PJ_DEF(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm)
{
PJ_ASSERT_RETURN(prm != NULL, PJ_EINVAL);
PjSymbianOS::Instance()->SetParameters(prm);
return PJ_SUCCESS;
}
/* Set connection status */
PJ_DEF(void) pj_symbianos_set_connection_status(pj_bool_t up)
{
PjSymbianOS::Instance()->SetConnectionStatus(up != 0);
}
/*
* pj_init(void).
* Init PJLIB!
*/
PJ_DEF(pj_status_t) pj_init(void)
{
char stack_ptr;
pj_status_t status;
/* Check if PJLIB have been initialized */
if (initialized) {
++initialized;
return PJ_SUCCESS;
}
pj_ansi_strcpy(main_thread.obj_name, "pjthread");
// Init main thread
pj_memset(&main_thread, 0, sizeof(main_thread));
// Initialize PjSymbianOS instance
PjSymbianOS *os = PjSymbianOS::Instance();
PJ_LOG(4,(THIS_FILE, "Initializing PJLIB for Symbian OS.."));
TInt err;
err = os->Initialize();
if (err != KErrNone)
return PJ_RETURN_OS_ERROR(err);
/* Init logging */
pj_log_init();
/* Initialize exception ID for the pool.
* Must do so after critical section is configured.
*/
status = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
if (status != PJ_SUCCESS)
goto on_error;
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
main_thread.stk_start = &stack_ptr;
main_thread.stk_size = 0xFFFFFFFFUL;
main_thread.stk_max_usage = 0;
#else
stack_ptr = '\0';
#endif
/* Flag PJLIB as initialized */
++initialized;
pj_assert(initialized == 1);
PJ_LOG(5,(THIS_FILE, "PJLIB initialized."));
return PJ_SUCCESS;
on_error:
pj_shutdown();
return PJ_RETURN_OS_ERROR(err);
}
PJ_DEF(pj_status_t) pj_atexit(pj_exit_callback func)
{
if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
return PJ_ETOOMANY;
atexit_func[atexit_count++] = func;
return PJ_SUCCESS;
}
PJ_DEF(void) pj_shutdown(void)
{
/* Only perform shutdown operation when 'initialized' reaches zero */
pj_assert(initialized > 0);
if (--initialized != 0)
return;
/* Call atexit() functions */
while (atexit_count > 0) {
(*atexit_func[atexit_count-1])();
--atexit_count;
}
/* Free exception ID */
if (PJ_NO_MEMORY_EXCEPTION != -1) {
pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
PJ_NO_MEMORY_EXCEPTION = -1;
}
/* Clear static variables */
pj_errno_clear_handlers();
PjSymbianOS *os = PjSymbianOS::Instance();
os->Shutdown();
}
/////////////////////////////////////////////////////////////////////////////
class CPollTimeoutTimer : public CActive
{
public:
static CPollTimeoutTimer* NewL(int msec, TInt prio);
~CPollTimeoutTimer();
virtual void RunL();
virtual void DoCancel();
private:
RTimer rtimer_;
explicit CPollTimeoutTimer(TInt prio);
void ConstructL(int msec);
};
CPollTimeoutTimer::CPollTimeoutTimer(TInt prio)
: CActive(prio)
{
}
CPollTimeoutTimer::~CPollTimeoutTimer()
{
rtimer_.Close();
}
void CPollTimeoutTimer::ConstructL(int msec)
{
rtimer_.CreateLocal();
CActiveScheduler::Add(this);
rtimer_.After(iStatus, msec*1000);
SetActive();
}
CPollTimeoutTimer* CPollTimeoutTimer::NewL(int msec, TInt prio)
{
CPollTimeoutTimer *self = new CPollTimeoutTimer(prio);
CleanupStack::PushL(self);
self->ConstructL(msec);
CleanupStack::Pop(self);
return self;
}
void CPollTimeoutTimer::RunL()
{
}
void CPollTimeoutTimer::DoCancel()
{
rtimer_.Cancel();
}
/*
* Wait the completion of any Symbian active objects.
*/
PJ_DEF(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout)
{
CPollTimeoutTimer *timer = NULL;
if (priority==-1)
priority = EPriorityNull;
if (ms_timeout >= 0) {
timer = CPollTimeoutTimer::NewL(ms_timeout, priority);
}
PjSymbianOS::Instance()->WaitForActiveObjects(priority);
if (timer) {
bool timer_is_active = timer->IsActive();
timer->Cancel();
delete timer;
return timer_is_active ? PJ_TRUE : PJ_FALSE;
} else {
return PJ_TRUE;
}
}
/*
* pj_thread_is_registered()
*/
PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
{
return PJ_FALSE;
}
/*
* Get thread priority value for the thread.
*/
PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
{
PJ_UNUSED_ARG(thread);
return 1;
}
/*
* Set the thread priority.
*/
PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio)
{
PJ_UNUSED_ARG(thread);
PJ_UNUSED_ARG(prio);
return PJ_SUCCESS;
}
/*
* Get the lowest priority value available on this system.
*/
PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
{
PJ_UNUSED_ARG(thread);
return 1;
}
/*
* Get the highest priority value available on this system.
*/
PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
{
PJ_UNUSED_ARG(thread);
return 1;
}
/*
* pj_thread_get_os_handle()
*/
PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread)
{
PJ_UNUSED_ARG(thread);
return NULL;
}
/*
* pj_thread_register(..)
*/
PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
pj_thread_desc desc,
pj_thread_t **thread_ptr)
{
PJ_UNUSED_ARG(cstr_thread_name);
PJ_UNUSED_ARG(desc);
PJ_UNUSED_ARG(thread_ptr);
return PJ_EINVALIDOP;
}
/*
* pj_thread_create(...)
*/
PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
const char *thread_name,
pj_thread_proc *proc,
void *arg,
pj_size_t stack_size,
unsigned flags,
pj_thread_t **ptr_thread)
{
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(thread_name);
PJ_UNUSED_ARG(proc);
PJ_UNUSED_ARG(arg);
PJ_UNUSED_ARG(stack_size);
PJ_UNUSED_ARG(flags);
PJ_UNUSED_ARG(ptr_thread);
/* Sorry mate, we don't support threading */
return PJ_ENOTSUP;
}
/*
* pj_thread-get_name()
*/
PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
{
pj_assert(p == &main_thread);
return p->obj_name;
}
/*
* pj_thread_resume()
*/
PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
{
PJ_UNUSED_ARG(p);
return PJ_EINVALIDOP;
}
/*
* pj_thread_this()
*/
PJ_DEF(pj_thread_t*) pj_thread_this(void)
{
return &main_thread;
}
/*
* pj_thread_join()
*/
PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *rec)
{
PJ_UNUSED_ARG(rec);
return PJ_EINVALIDOP;
}
/*
* pj_thread_destroy()
*/
PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *rec)
{
PJ_UNUSED_ARG(rec);
return PJ_EINVALIDOP;
}
/*
* pj_thread_sleep()
*/
PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
{
User::After(msec*1000);
return PJ_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
/*
* pj_thread_local_alloc()
*/
PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
{
unsigned i;
/* Find unused TLS variable */
for (i=0; i<PJ_ARRAY_SIZE(tls_vars); ++i) {
if (tls_vars[i] == 0)
break;
}
if (i == PJ_ARRAY_SIZE(tls_vars))
return PJ_ETOOMANY;
tls_vars[i] = 1;
*index = i;
return PJ_SUCCESS;
}
/*
* pj_thread_local_free()
*/
PJ_DEF(void) pj_thread_local_free(long index)
{
PJ_ASSERT_ON_FAIL(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
tls_vars[index] != 0, return);
tls_vars[index] = 0;
}
/*
* pj_thread_local_set()
*/
PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
{
pj_thread_t *rec = pj_thread_this();
PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
tls_vars[index] != 0, PJ_EINVAL);
rec->tls_values[index] = value;
return PJ_SUCCESS;
}
/*
* pj_thread_local_get()
*/
PJ_DEF(void*) pj_thread_local_get(long index)
{
pj_thread_t *rec = pj_thread_this();
PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
tls_vars[index] != 0, NULL);
return rec->tls_values[index];
}
///////////////////////////////////////////////////////////////////////////////
/*
* Create atomic variable.
*/
PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
pj_atomic_value_t initial,
pj_atomic_t **atomic )
{
*atomic = (pj_atomic_t*)pj_pool_alloc(pool, sizeof(struct pj_atomic_t));
(*atomic)->value = initial;
return PJ_SUCCESS;
}
/*
* Destroy atomic variable.
*/
PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
{
PJ_UNUSED_ARG(atomic_var);
return PJ_SUCCESS;
}
/*
* Set the value of an atomic type, and return the previous value.
*/
PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var,
pj_atomic_value_t value)
{
atomic_var->value = value;
}
/*
* Get the value of an atomic type.
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
{
return atomic_var->value;
}
/*
* Increment the value of an atomic type.
*/
PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
{
++atomic_var->value;
}
/*
* Increment the value of an atomic type and get the result.
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
{
return ++atomic_var->value;
}
/*
* Decrement the value of an atomic type.
*/
PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
{
--atomic_var->value;
}
/*
* Decrement the value of an atomic type and get the result.
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
{
return --atomic_var->value;
}
/*
* Add a value to an atomic type.
*/
PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
pj_atomic_value_t value)
{
atomic_var->value += value;
}
/*
* Add a value to an atomic type and get the result.
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
pj_atomic_value_t value)
{
atomic_var->value += value;
return atomic_var->value;
}
/////////////////////////////////////////////////////////////////////////////
PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool,
const char *name,
int type,
pj_mutex_t **mutex)
{
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(name);
PJ_UNUSED_ARG(type);
*mutex = DUMMY_MUTEX;
return PJ_SUCCESS;
}
/*
* pj_mutex_create_simple()
*/
PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
const char *name,
pj_mutex_t **mutex )
{
return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
}
PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
const char *name,
pj_mutex_t **mutex )
{
return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
}
/*
* pj_mutex_lock()
*/
PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
{
pj_assert(mutex == DUMMY_MUTEX);
return PJ_SUCCESS;
}
/*
* pj_mutex_trylock()
*/
PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
{
pj_assert(mutex == DUMMY_MUTEX);
return PJ_SUCCESS;
}
/*
* pj_mutex_unlock()
*/
PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
{
pj_assert(mutex == DUMMY_MUTEX);
return PJ_SUCCESS;
}
/*
* pj_mutex_destroy()
*/
PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
{
pj_assert(mutex == DUMMY_MUTEX);
return PJ_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////
/*
* RW Mutex
*/
#include "os_rwmutex.c"
/////////////////////////////////////////////////////////////////////////////
/*
* Enter critical section.
*/
PJ_DEF(void) pj_enter_critical_section(void)
{
/* Nothing to do */
}
/*
* Leave critical section.
*/
PJ_DEF(void) pj_leave_critical_section(void)
{
/* Nothing to do */
}
/////////////////////////////////////////////////////////////////////////////
/*
* Create semaphore.
*/
PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
const char *name,
unsigned initial,
unsigned max,
pj_sem_t **p_sem)
{
pj_sem_t *sem;
PJ_UNUSED_ARG(name);
sem = (pj_sem_t*) pj_pool_zalloc(pool, sizeof(pj_sem_t));
sem->value = initial;
sem->max = max;
*p_sem = sem;
return PJ_SUCCESS;
}
/*
* Wait for semaphore.
*/
PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
{
if (sem->value > 0) {
sem->value--;
return PJ_SUCCESS;
} else {
pj_assert(!"Unexpected!");
return PJ_EINVALIDOP;
}
}
/*
* Try wait for semaphore.
*/
PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
{
if (sem->value > 0) {
sem->value--;
return PJ_SUCCESS;
} else {
pj_assert(!"Unexpected!");
return PJ_EINVALIDOP;
}
}
/*
* Release semaphore.
*/
PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
{
sem->value++;
return PJ_SUCCESS;
}
/*
* Destroy semaphore.
*/
PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
{
PJ_UNUSED_ARG(sem);
return PJ_SUCCESS;
}
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
/*
* The implementation of stack checking.
*/
PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
{
char stk_ptr;
pj_uint32_t usage;
pj_thread_t *thread = pj_thread_this();
pj_assert(thread);
/* Calculate current usage. */
usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
thread->stk_start - &stk_ptr;
/* Assert if stack usage is dangerously high. */
pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
/* Keep statistic. */
if (usage > thread->stk_max_usage) {
thread->stk_max_usage = usage;
thread->caller_file = file;
thread->caller_line = line;
}
}
/*
* Get maximum stack usage statistic.
*/
PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
{
return thread->stk_max_usage;
}
/*
* Dump thread stack status.
*/
PJ_DEF(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread,
const char **file,
int *line)
{
pj_assert(thread);
*file = thread->caller_file;
*line = thread->caller_line;
return 0;
}
#endif /* PJ_OS_HAS_CHECK_STACK */
/*
* pj_run_app()
*/
PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
unsigned flags)
{
return (*main_func)(argc, argv);
}