
/*---------------------------------------------------------------------------*/
/* stask_threads.cpp                                                         */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include <pthread.h>
#include "sdk/common/os/iomux.h"
#include "sdk/common/interface/task.h"
#include "sdk/common/interface/stask.h"
#include "stask_threads.h"

#define DBG(x) //debug->printf x

/*---------------------------------------------------------------------------*/
/* STask                                                                     */
/*---------------------------------------------------------------------------*/

STask::STask(class STaskContext * context)
{
    this->context = context;
    thread = NULL;
}

STask::~STask()
{
    ASSERT(!thread, "STask::~STask deleted without waiting for complete or failed");
}

void STask::Start(class UTask * user)
{
    DBG(("%x:STask::Start %x", this, context->threadsIdle.front()));
    this->user = user;
    if (!context->threadsIdle.empty()) {
        thread = context->threadsIdle.pop_front();
        thread->startTask(this);
        context->threadsBusy.push_back(thread);
    }
    else {
        thread = new STaskLinuxThread(this);
        context->threadsBusy.push_back(thread);
    }
}

void STask::TaskComplete()
{
    DBG(("%x:STask::TaskComplete", this));
    context->iomux->SetExecLocked((UIoExecSubclass<STaskComplete> *) this, (STaskComplete *)NULL);
}

void STask::TaskFailed()
{
    DBG(("%x:STask::TaskFailed", this));
    context->iomux->SetExecLocked((UIoExecSubclass<STaskFailed> *) this, (STaskFailed *)NULL);
}

void STask::TaskProgress(dword progress)
{
    DBG(("%x:STask::TaskProgress(%x)", this, progress));
    context->iomux->SetExecLocked((UIoExecSubclass<STaskProgress> *) this, (STaskProgress *)(uintp)progress);
}

void STask::IoExec(STaskComplete * execContext)
{
    DBG(("%x:STask::IoExecTaskComplete", this));
    thread->remove();
    context->threadsIdle.push_back(thread);
    thread = NULL;
    user->TaskComplete(this);
}

void STask::IoExec(STaskFailed * execContext)
{
    DBG(("%x:STask::IoExecTaskFailed", this));
    thread->remove();
    context->threadsIdle.push_back(thread);
    thread = NULL;
    user->TaskFailed(this);
}

void STask::IoExec(STaskProgress * execContext)
{
    DBG(("%x:STask::IoExecTaskProgress(%x)", this, (dword)(((byte *) execContext) - ((byte *) 0))));
    user->TaskProgress(this, (dword)(((byte *) execContext) - ((byte *) 0)));
}

/*---------------------------------------------------------------------------*/
/* STaskLinux                                                                */
/*---------------------------------------------------------------------------*/

void *__thread_main(void * ThreadObjPtr)
{
    ((class STaskLinuxThread *)ThreadObjPtr)->exec();
    return 0;
}

STaskLinuxThread::STaskLinuxThread(class STask * task)
{
    ASSERT(task, "STaskLinuxThread::STaskLinuxThread no user");
    this->task = task;
    started = true;
    ended = false;
    pthread_create(&threadID, NULL, __thread_main, this);
}

STaskLinuxThread::~STaskLinuxThread()
{
    pthread_mutex_lock(&lock);
    started = true;
    ended = true;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);
    pthread_join(threadID, NULL);
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&lock);
}

void STaskLinuxThread::startTask(class STask * task)
{
    ASSERT(task, "STaskLinuxThread::startTask no user");
    this->task = task;
    cont();
}

void STaskLinuxThread::exec()
{
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_mutex_lock(&lock);
    do {
        started = false;
        pthread_mutex_unlock(&lock);
        DBG(("%x:STaskLinux::exec", task));
        task->SStart();
        pthread_mutex_lock(&lock);
        if (!started) pthread_cond_wait(&cond, &lock);
    } while (!ended);
    pthread_mutex_unlock(&lock);
}

void STaskLinuxThread::wait()
{
    DBG(("%x:STaskLinux::wait", task));
    pthread_mutex_lock(&lock);
    if (!started) pthread_cond_wait(&cond, &lock);
    started = false;
    pthread_mutex_unlock(&lock);
}

void STaskLinuxThread::cont()
{
    DBG(("%x:STaskLinux::cont", task));
    pthread_mutex_lock(&lock);
    started = true;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);
}
