/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _MOTTHREADS_H
#define _MOTTHREADS_H

#include <stdint.h>
#include <sys/types.h>
#include <time.h>

# include <cutils/mot_pthread.h>

// ------------------------------------------------------------------
// C++ API

#ifdef __cplusplus

#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>

namespace android {

/*****************************************************************************/

/*
 * Simple mutex class.  The implementation is system-dependent.
 *
 * The mutex must be unlocked by the thread that locked it.  They are not
 * recursive, i.e. the same thread can't lock it multiple times.
 */
class MotMutex {
public:
    enum {
        PRIVATE = 0,
        SHARED = 1
    };
    
                MotMutex() {}
                MotMutex(const char* name) {}
                MotMutex(int type, const char* name = NULL) {}
                ~MotMutex() {}

    // lock or unlock the mutex
    status_t    lock();
    void        unlock();

    // lock if possible; returns 0 on success, error otherwise
    status_t    tryLock();

    // Manages the mutex automatically. It'll be locked when Autolock is
    // constructed and released when Autolock goes out of scope.
    class Autolock {
    public:
        inline Autolock(MotMutex& mutex) : mLock(mutex)  { mLock.lock(); }
        inline Autolock(MotMutex* mutex) : mLock(*mutex) { mLock.lock(); }
        inline ~Autolock() { mLock.unlock(); }
    private:
        MotMutex& mLock;
    };

private:
    friend class MotCondition;
    
    // A mutex cannot be copied
                MotMutex(const MotMutex&);
    MotMutex&      operator = (const MotMutex&);
    
    mot_pthread_mutex_t mMutex;
};

inline status_t MotMutex::lock() {
    return -mot_pthread_mutex_lock(&mMutex);
}
inline void MotMutex::unlock() {
    mot_pthread_mutex_unlock(&mMutex);
}
inline status_t MotMutex::tryLock() {
    return -mot_pthread_mutex_trylock(&mMutex);
}

/*
 * Automatic mutex.  Declare one of these at the top of a function.
 * When the function returns, it will go out of scope, and release the
 * mutex.
 */
 
typedef MotMutex::Autolock AutoMotMutex;

/*****************************************************************************/

/*
 * Condition variable class.  The implementation is system-dependent.
 *
 * Condition variables are paired up with mutexes.  Lock the mutex,
 * call wait(), then either re-wait() if things aren't quite what you want,
 * or unlock the mutex and continue.  All threads calling wait() must
 * use the same mutex for a given Condition.
 */
class MotCondition {
public:
        enum {
        PRIVATE = 0,
        SHARED = 1
    };

    MotCondition() {}
    MotCondition(int type) {}
    ~MotCondition() {}
    // Wait on the condition variable.  Lock the mutex before calling.
    status_t wait(MotMutex& mutex);
    // same with relative timeout
    status_t waitRelative(MotMutex& mutex, nsecs_t reltime);
    // Signal the condition variable, allowing one thread to continue.
    void signal();
    // Signal the condition variable, allowing all threads to continue.
    void broadcast();

private:
    mot_pthread_cond_t mCond;
};

inline status_t MotCondition::wait(MotMutex& mutex) {
    return -mot_pthread_cond_wait(&mCond, &mutex.mMutex);
}
inline status_t MotCondition::waitRelative(MotMutex& mutex, nsecs_t reltime) {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
    struct timespec ts;
    ts.tv_sec  = reltime/1000000000;
    ts.tv_nsec = reltime%1000000000;
    return -mot_pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
    struct timespec ts;
#if defined(HAVE_POSIX_CLOCKS)
    clock_gettime(CLOCK_REALTIME, &ts);
#else // HAVE_POSIX_CLOCKS
    // we don't support the clocks here.
    struct timeval t;
    gettimeofday(&t, NULL);
    ts.tv_sec = t.tv_sec;
    ts.tv_nsec= t.tv_usec*1000;
#endif // HAVE_POSIX_CLOCKS
    ts.tv_sec += reltime/1000000000;
    ts.tv_nsec+= reltime%1000000000;
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_nsec -= 1000000000;
        ts.tv_sec  += 1;
    }
    return -mot_pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE

    return 0;
}
inline void MotCondition::signal() {
    mot_pthread_cond_signal(&mCond);
}
inline void MotCondition::broadcast() {
    mot_pthread_cond_broadcast(&mCond);
}

}; // namespace android

#endif  // __cplusplus

#endif // _MOTTHREADS_H
