/* $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);
}
