<commit_before>// -------------------------------------------------------------------
//
// hot_threads.cc
//
// Copyright (c) 2011-2015 Basho Technologies, Inc. All Rights Reserved.
//
// This file is provided to you 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.
//
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// HotThread is a subtle variation on the eleveldb_thread_pool.  Both
//  represent a design pattern that is tested to perform better under
//  the Erlang VM than other traditional designs.
// -------------------------------------------------------------------

#include <errno.h>
#include <syslog.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "leveldb/atomics.h"
#include "util/hot_threads.h"
#include "util/thread_tasks.h"

namespace leveldb {

HotThreadPool * gImmThreads=NULL;
HotThreadPool * gWriteThreads=NULL;
HotThreadPool * gLevel0Threads=NULL;
HotThreadPool * gCompactionThreads=NULL;

void *ThreadStaticEntry(void *args)
{
    HotThread &tdata = *(HotThread *)args;

    return(tdata.ThreadRoutine());

}   // ThreadStaticEntry

/**
 * Worker threads:  worker threads have 3 states:
 *  A. doing nothing, available to be claimed: m_Available=1
 *  B. processing work passed by Erlang thread: m_Available=0, m_DirectWork=<non-null>
 *  C. processing backlog queue of work: m_Available=0, m_DirectWork=NULL
 */
void *
HotThread::ThreadRoutine()
{
    ThreadTask * submission;

    submission=NULL;

    port::SetCurrentThreadName(m_Pool.m_PoolName.c_str());

    if (0!=m_Nice)
    {
        pid_t tid;
        int ret_val;

        tid = syscall(SYS_gettid);
        ret_val=getpriority(PRIO_PROCESS, tid);
        setpriority(PRIO_PROCESS, tid, ret_val+m_Nice);
    }   // if

    while(!m_Pool.m_Shutdown)
    {
        // is work assigned yet?
        //  check backlog work queue if not
        if (NULL==submission)
        {
            // test non-blocking size for hint (much faster)
            if (0!=m_Pool.m_WorkQueueAtomic)
            {
                // retest with locking
                SpinLock lock(&m_Pool.m_QueueLock);

                if (!m_Pool.m_WorkQueue.empty())
                {
                    submission=m_Pool.m_WorkQueue.front();
                    m_Pool.m_WorkQueue.pop_front();
                    dec_and_fetch(&m_Pool.m_WorkQueueAtomic);
                    m_Pool.IncWorkDequeued();
                    m_Pool.IncWorkWeighted(Env::Default()->NowMicros()
                                           - submission->m_QueueStart);
                }   // if
            }   // if
        }   // if

        // a work item identified (direct or queue), work it!
        //  then loop to test queue again
        if (NULL!=submission)
        {
            // execute the job
            (*submission)();
            if (submission->resubmit())
            {
                submission->recycle();
                m_Pool.Submit(submission);
            }

            submission->RefDec();

            submission=NULL;
        }   // if

        // no work found, attempt to go into wait state
        //  (but retest queue before sleep due to race condition)
        else
        {
            MutexLock lock(&m_Mutex);

            m_DirectWork=NULL; // safety

            // only wait if we are really sure no work pending
            if (0==m_Pool.m_WorkQueueAtomic)
            {
                // yes, thread going to wait. set available now.
                m_Available=1;
                m_Condition.Wait();
            }    // if

            m_Available=0;    // safety
            submission=(ThreadTask *)m_DirectWork; // NULL is valid
            m_DirectWork=NULL;// safety
        }   // else
    }   // while

    return 0;

}   // HotThread::ThreadRoutine

void *QueueThreadStaticEntry(void *args)
{
    QueueThread &tdata = *(QueueThread *)args;

    return(tdata.QueueThreadRoutine());

}   // QueueThreadStaticEntry

QueueThread::QueueThread(
    class HotThreadPool & Pool, int Nice)
    : m_ThreadGood(false), m_Pool(Pool), m_Nice(Nice), m_SemaphorePtr(NULL)
{
    int ret_val;

    m_QueueName=m_Pool.m_PoolName;
    m_QueueName.append("Semaphore");

    memset(&m_Semaphore, 0, sizeof(m_Semaphore));
    ret_val=sem_init(&m_Semaphore, 0, 0);

    if (0==ret_val)
    {
        m_SemaphorePtr=&m_Semaphore;
    }   // if

    // retry with named semaphore
    else if (0!=ret_val && ENOSYS==errno)
    {
        char pid_str[32];
        snprintf(pid_str, sizeof(pid_str), "%d", (int)getpid());
        m_QueueName.append(pid_str);

        m_SemaphorePtr=sem_open(m_QueueName.c_str(), O_CREAT | O_EXCL,
                                S_IRUSR | S_IWUSR, 0);
        // attempt delete and retry blindly
        if (SEM_FAILED==m_SemaphorePtr)
        {
            sem_unlink(m_QueueName.c_str());
            m_SemaphorePtr=sem_open(m_QueueName.c_str(), O_CREAT | O_EXCL,
                                    S_IRUSR | S_IWUSR, 0);
        }   // if

        // so ret_val will be zero on success
        ret_val=(SEM_FAILED==m_SemaphorePtr);
    }   // else if

    // only start this if we have a working semaphore
    if (0==ret_val)
    {
        ret_val=pthread_create(&m_ThreadId, NULL,  &QueueThreadStaticEntry, this);
        if (0==ret_val)
        {
            m_ThreadGood=true;
        }   // if
        else
        {
            syslog(LOG_ERR, "thread_create failed in QueueThread::QueueThread [%d, %m]",
                   errno);
            gPerfCounters->Inc(ePerfThreadError);

            if (&m_Semaphore==m_SemaphorePtr)
            {
                sem_destroy(&m_Semaphore);
            }   // if
            else
            {
                sem_close(m_SemaphorePtr);
                sem_unlink(m_QueueName.c_str());
            }   // else

            m_SemaphorePtr=NULL;
        }   // else
    }   // if
    else
    {
        m_SemaphorePtr=NULL;
        syslog(LOG_ERR, "sem_init failed in QueueThread::QueueThread [%d, %m]",
               errno);
        gPerfCounters->Inc(ePerfThreadError);
    }   // else

    return;

}   // QueueThread::QueueThread

QueueThread::~QueueThread()
{
    // only clean up resources if they were started
    if (m_ThreadGood)
    {
        // release the m_QueueThread to exit
        sem_post(m_SemaphorePtr);
        pthread_join(m_ThreadId, NULL);

        if (&m_Semaphore==m_SemaphorePtr)
        {
            sem_destroy(&m_Semaphore);
        }   // if
        else
        {
            sem_close(m_SemaphorePtr);
            sem_unlink(m_QueueName.c_str());
        }   // else
    }   // if

    return;
}   // QueueThread::~QueueThread

/**
 * Cover highly inprobable race condition with adding a queue
 *  and no hot thread sees it.
 */
void *
QueueThread::QueueThreadRoutine()
{
    ThreadTask * submission;

    submission=NULL;

    port::SetCurrentThreadName(m_QueueName.c_str());

    if (0!=m_Nice)
    {
        pid_t tid;
        int ret_val;

        tid = syscall(SYS_gettid);
        ret_val=getpriority(PRIO_PROCESS, tid);
        setpriority(PRIO_PROCESS, tid, ret_val+m_Nice);
    }   // if

    while(!m_Pool.m_Shutdown)
    {
        // test non-blocking size for hint (much faster)
        if (0!=m_Pool.m_WorkQueueAtomic)
        {
            // retest with locking
            SpinLock lock(&m_Pool.m_QueueLock);

            if (!m_Pool.m_WorkQueue.empty())
            {
                submission=m_Pool.m_WorkQueue.front();
                m_Pool.m_WorkQueue.pop_front();
                dec_and_fetch(&m_Pool.m_WorkQueueAtomic);
                m_Pool.IncWorkDequeued();
                m_Pool.IncWorkWeighted(Env::Default()->NowMicros()
                                       - submission->m_QueueStart);
            }   // if
        }   // if

        // a work item identified (direct or queue), work it!
        //  then loop to test queue again
        if (NULL!=submission)
        {
            // execute the job
            (*submission)();

            submission->RefDec();

            submission=NULL;
        }   // if

        // loop until semaphore hits zero
        sem_wait(m_SemaphorePtr);

    }   // while

    return 0;

}   // QueueThread::QueueThreadRoutine

HotThreadPool::HotThreadPool(
    const size_t PoolSize,
    const char * Name,
    enum PerformanceCountersEnum Direct,
    enum PerformanceCountersEnum Queued,
    enum PerformanceCountersEnum Dequeued,
    enum PerformanceCountersEnum Weighted,
    int Nice)
    : m_PoolName((Name?Name:"")),    // this crashes if Name is NULL ...but need it set now
      m_Shutdown(false),
      m_WorkQueueAtomic(0), m_QueueThread(*this, Nice),
      m_DirectCounter(Direct), m_QueuedCounter(Queued),
      m_DequeuedCounter(Dequeued), m_WeightedCounter(Weighted)
{
    int ret_val;
    size_t loop;
    HotThread * hot_ptr;

    ret_val=0;
    for (loop=0; loop<PoolSize && 0==ret_val; ++loop)
    {
        hot_ptr=new HotThread(*this, Nice);

        ret_val=pthread_create(&hot_ptr->m_ThreadId, NULL,  &ThreadStaticEntry, hot_ptr);
        if (0==ret_val)
            m_Threads.push_back(hot_ptr);
        else
            delete hot_ptr;
    }   // for

    m_Shutdown=(0!=ret_val);

    return;

}   // HotThreadPool::HotThreadPool

HotThreadPool::~HotThreadPool()
{
    ThreadPool_t::iterator thread_it;
    WorkQueue_t::iterator work_it;
    // set flag
    m_Shutdown=true;

    // get all threads stopped
    for (thread_it=m_Threads.begin(); m_Threads.end()!=thread_it; ++thread_it)
    {
        {
            MutexLock lock(&(*thread_it)->m_Mutex);
            (*thread_it)->m_Condition.SignalAll();
        }   // lock

        pthread_join((*thread_it)->m_ThreadId, NULL);
        delete *thread_it;
    }   // for

    // release any objects hanging in work queue
    for (work_it=m_WorkQueue.begin(); m_WorkQueue.end()!=work_it; ++work_it)
    {
        (*work_it)->RefDec();
    }   // for

    return;

}   // HotThreadPool::~HotThreadPool

bool                           // returns true if available worker thread found and claimed
HotThreadPool::FindWaitingThread(
    ThreadTask * work, // non-NULL to pass current work directly to a thread,
                       // NULL to potentially nudge an available worker toward backlog queue
    bool OkToQueue)
{
    bool ret_flag;
    size_t start, index, pool_size;

    ret_flag=false;

    // pick "random" place in thread list.  hopefully
    //  list size is prime number.
    pool_size=m_Threads.size();
    if (OkToQueue)
        start=(size_t)pthread_self() % pool_size;
    else
        start=0;
    index=start;

    do
    {
        // perform quick test to see thread available
        if (0!=m_Threads[index]->m_Available && !shutdown_pending())
        {
            // perform expensive compare and swap to potentially
            //  claim worker thread (this is an exclusive claim to the worker)
            ret_flag = compare_and_swap(&m_Threads[index]->m_Available, 1, 0);

            // the compare/swap only succeeds if worker thread is sitting on
            //  pthread_cond_wait ... or is about to be there but is holding
            //  the mutex already
            if (ret_flag)
            {

                // man page says mutex lock optional, experience in
                //  this code says it is not.  using broadcast instead
                //  of signal to cover one other race condition
                //  that should never happen with single thread waiting.
                MutexLock lock(&m_Threads[index]->m_Mutex);
                m_Threads[index]->m_DirectWork=work;
                m_Threads[index]->m_Condition.SignalAll();
            }   // if
        }   // if

        index=(index+1)%pool_size;

    } while(index!=start && !ret_flag && OkToQueue);

    return(ret_flag);

}   // FindWaitingThread

bool
HotThreadPool::Submit(
    ThreadTask* item,
    bool OkToQueue)
{
    bool ret_flag(false);
    int ret_val;

    if (NULL!=item)
    {
        item->RefInc();

        // do nothing if shutting down
        if(shutdown_pending())
        {
            item->RefDec();
            ret_flag=false;
        }   // if

        // try to give work to a waiting thread first
        else if (FindWaitingThread(item, OkToQueue))
        {
            IncWorkDirect();
            ret_flag=true;
        }   // else if

        else if (OkToQueue)
        {
            item->m_QueueStart=Env::Default()->NowMicros();

            // no waiting threads, put on backlog queue
            {
                SpinLock lock(&m_QueueLock);
                inc_and_fetch(&m_WorkQueueAtomic);
                m_WorkQueue.push_back(item);
            }

            // to address race condition, thread might be waiting now
            FindWaitingThread(NULL, true);

            // to address second race condition, send in QueueThread
            //   (thread not likely good on OSX)
            if (m_QueueThread.m_ThreadGood)
            {
                ret_val=sem_post(m_QueueThread.m_SemaphorePtr);
                if (0!=ret_val)
                {
                    syslog(LOG_ERR, "sem_post failed in HotThreadPool::Submit [%d, %m]",
                           errno);
                    gPerfCounters->Inc(ePerfThreadError);
                }   // if
            }   // if

            IncWorkQueued();
            ret_flag=true;
        }   // else if

        // did not post to thread or queue
        else
        {
            item->RefDec();
            ret_flag=false;  // redundant, but safe
        }   // else
    }   // if

    return(ret_flag);

}   // HotThreadPool::Submit

};  // namespace leveldb
<commit_msg>Put conditional compile around Nice code until validated on other platforms.  Do not want to nice down the entire process.<commit_after>// -------------------------------------------------------------------
//
// hot_threads.cc
//
// Copyright (c) 2011-2015 Basho Technologies, Inc. All Rights Reserved.
//
// This file is provided to you 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.
//
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// HotThread is a subtle variation on the eleveldb_thread_pool.  Both
//  represent a design pattern that is tested to perform better under
//  the Erlang VM than other traditional designs.
// -------------------------------------------------------------------

#include <errno.h>
#include <syslog.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "leveldb/atomics.h"
#include "util/hot_threads.h"
#include "util/thread_tasks.h"

namespace leveldb {

HotThreadPool * gImmThreads=NULL;
HotThreadPool * gWriteThreads=NULL;
HotThreadPool * gLevel0Threads=NULL;
HotThreadPool * gCompactionThreads=NULL;

void *ThreadStaticEntry(void *args)
{
    HotThread &tdata = *(HotThread *)args;

    return(tdata.ThreadRoutine());

}   // ThreadStaticEntry

/**
 * Worker threads:  worker threads have 3 states:
 *  A. doing nothing, available to be claimed: m_Available=1
 *  B. processing work passed by Erlang thread: m_Available=0, m_DirectWork=<non-null>
 *  C. processing backlog queue of work: m_Available=0, m_DirectWork=NULL
 */
void *
HotThread::ThreadRoutine()
{
    ThreadTask * submission;

    submission=NULL;

    port::SetCurrentThreadName(m_Pool.m_PoolName.c_str());
#ifdef OS_LINUX
    if (0!=m_Nice)
    {
        pid_t tid;
        int ret_val;

        tid = syscall(SYS_gettid);
        ret_val=getpriority(PRIO_PROCESS, tid);
        setpriority(PRIO_PROCESS, tid, ret_val+m_Nice);
    }   // if
#endif
    while(!m_Pool.m_Shutdown)
    {
        // is work assigned yet?
        //  check backlog work queue if not
        if (NULL==submission)
        {
            // test non-blocking size for hint (much faster)
            if (0!=m_Pool.m_WorkQueueAtomic)
            {
                // retest with locking
                SpinLock lock(&m_Pool.m_QueueLock);

                if (!m_Pool.m_WorkQueue.empty())
                {
                    submission=m_Pool.m_WorkQueue.front();
                    m_Pool.m_WorkQueue.pop_front();
                    dec_and_fetch(&m_Pool.m_WorkQueueAtomic);
                    m_Pool.IncWorkDequeued();
                    m_Pool.IncWorkWeighted(Env::Default()->NowMicros()
                                           - submission->m_QueueStart);
                }   // if
            }   // if
        }   // if

        // a work item identified (direct or queue), work it!
        //  then loop to test queue again
        if (NULL!=submission)
        {
            // execute the job
            (*submission)();
            if (submission->resubmit())
            {
                submission->recycle();
                m_Pool.Submit(submission);
            }

            submission->RefDec();

            submission=NULL;
        }   // if

        // no work found, attempt to go into wait state
        //  (but retest queue before sleep due to race condition)
        else
        {
            MutexLock lock(&m_Mutex);

            m_DirectWork=NULL; // safety

            // only wait if we are really sure no work pending
            if (0==m_Pool.m_WorkQueueAtomic)
            {
                // yes, thread going to wait. set available now.
                m_Available=1;
                m_Condition.Wait();
            }    // if

            m_Available=0;    // safety
            submission=(ThreadTask *)m_DirectWork; // NULL is valid
            m_DirectWork=NULL;// safety
        }   // else
    }   // while

    return 0;

}   // HotThread::ThreadRoutine

void *QueueThreadStaticEntry(void *args)
{
    QueueThread &tdata = *(QueueThread *)args;

    return(tdata.QueueThreadRoutine());

}   // QueueThreadStaticEntry

QueueThread::QueueThread(
    class HotThreadPool & Pool, int Nice)
    : m_ThreadGood(false), m_Pool(Pool), m_Nice(Nice), m_SemaphorePtr(NULL)
{
    int ret_val;

    m_QueueName=m_Pool.m_PoolName;
    m_QueueName.append("Semaphore");

    memset(&m_Semaphore, 0, sizeof(m_Semaphore));
    ret_val=sem_init(&m_Semaphore, 0, 0);

    if (0==ret_val)
    {
        m_SemaphorePtr=&m_Semaphore;
    }   // if

    // retry with named semaphore
    else if (0!=ret_val && ENOSYS==errno)
    {
        char pid_str[32];
        snprintf(pid_str, sizeof(pid_str), "%d", (int)getpid());
        m_QueueName.append(pid_str);

        m_SemaphorePtr=sem_open(m_QueueName.c_str(), O_CREAT | O_EXCL,
                                S_IRUSR | S_IWUSR, 0);
        // attempt delete and retry blindly
        if (SEM_FAILED==m_SemaphorePtr)
        {
            sem_unlink(m_QueueName.c_str());
            m_SemaphorePtr=sem_open(m_QueueName.c_str(), O_CREAT | O_EXCL,
                                    S_IRUSR | S_IWUSR, 0);
        }   // if

        // so ret_val will be zero on success
        ret_val=(SEM_FAILED==m_SemaphorePtr);
    }   // else if

    // only start this if we have a working semaphore
    if (0==ret_val)
    {
        ret_val=pthread_create(&m_ThreadId, NULL,  &QueueThreadStaticEntry, this);
        if (0==ret_val)
        {
            m_ThreadGood=true;
        }   // if
        else
        {
            syslog(LOG_ERR, "thread_create failed in QueueThread::QueueThread [%d, %m]",
                   errno);
            gPerfCounters->Inc(ePerfThreadError);

            if (&m_Semaphore==m_SemaphorePtr)
            {
                sem_destroy(&m_Semaphore);
            }   // if
            else
            {
                sem_close(m_SemaphorePtr);
                sem_unlink(m_QueueName.c_str());
            }   // else

            m_SemaphorePtr=NULL;
        }   // else
    }   // if
    else
    {
        m_SemaphorePtr=NULL;
        syslog(LOG_ERR, "sem_init failed in QueueThread::QueueThread [%d, %m]",
               errno);
        gPerfCounters->Inc(ePerfThreadError);
    }   // else

    return;

}   // QueueThread::QueueThread

QueueThread::~QueueThread()
{
    // only clean up resources if they were started
    if (m_ThreadGood)
    {
        // release the m_QueueThread to exit
        sem_post(m_SemaphorePtr);
        pthread_join(m_ThreadId, NULL);

        if (&m_Semaphore==m_SemaphorePtr)
        {
            sem_destroy(&m_Semaphore);
        }   // if
        else
        {
            sem_close(m_SemaphorePtr);
            sem_unlink(m_QueueName.c_str());
        }   // else
    }   // if

    return;
}   // QueueThread::~QueueThread

/**
 * Cover highly inprobable race condition with adding a queue
 *  and no hot thread sees it.
 */
void *
QueueThread::QueueThreadRoutine()
{
    ThreadTask * submission;

    submission=NULL;

    port::SetCurrentThreadName(m_QueueName.c_str());
#ifdef OS_LINUX
    if (0!=m_Nice)
    {
        pid_t tid;
        int ret_val;

        tid = syscall(SYS_gettid);
        ret_val=getpriority(PRIO_PROCESS, tid);
        setpriority(PRIO_PROCESS, tid, ret_val+m_Nice);
    }   // if
#endif
    while(!m_Pool.m_Shutdown)
    {
        // test non-blocking size for hint (much faster)
        if (0!=m_Pool.m_WorkQueueAtomic)
        {
            // retest with locking
            SpinLock lock(&m_Pool.m_QueueLock);

            if (!m_Pool.m_WorkQueue.empty())
            {
                submission=m_Pool.m_WorkQueue.front();
                m_Pool.m_WorkQueue.pop_front();
                dec_and_fetch(&m_Pool.m_WorkQueueAtomic);
                m_Pool.IncWorkDequeued();
                m_Pool.IncWorkWeighted(Env::Default()->NowMicros()
                                       - submission->m_QueueStart);
            }   // if
        }   // if

        // a work item identified (direct or queue), work it!
        //  then loop to test queue again
        if (NULL!=submission)
        {
            // execute the job
            (*submission)();

            submission->RefDec();

            submission=NULL;
        }   // if

        // loop until semaphore hits zero
        sem_wait(m_SemaphorePtr);

    }   // while

    return 0;

}   // QueueThread::QueueThreadRoutine

HotThreadPool::HotThreadPool(
    const size_t PoolSize,
    const char * Name,
    enum PerformanceCountersEnum Direct,
    enum PerformanceCountersEnum Queued,
    enum PerformanceCountersEnum Dequeued,
    enum PerformanceCountersEnum Weighted,
    int Nice)
    : m_PoolName((Name?Name:"")),    // this crashes if Name is NULL ...but need it set now
      m_Shutdown(false),
      m_WorkQueueAtomic(0), m_QueueThread(*this, Nice),
      m_DirectCounter(Direct), m_QueuedCounter(Queued),
      m_DequeuedCounter(Dequeued), m_WeightedCounter(Weighted)
{
    int ret_val;
    size_t loop;
    HotThread * hot_ptr;

    ret_val=0;
    for (loop=0; loop<PoolSize && 0==ret_val; ++loop)
    {
        hot_ptr=new HotThread(*this, Nice);

        ret_val=pthread_create(&hot_ptr->m_ThreadId, NULL,  &ThreadStaticEntry, hot_ptr);
        if (0==ret_val)
            m_Threads.push_back(hot_ptr);
        else
            delete hot_ptr;
    }   // for

    m_Shutdown=(0!=ret_val);

    return;

}   // HotThreadPool::HotThreadPool

HotThreadPool::~HotThreadPool()
{
    ThreadPool_t::iterator thread_it;
    WorkQueue_t::iterator work_it;
    // set flag
    m_Shutdown=true;

    // get all threads stopped
    for (thread_it=m_Threads.begin(); m_Threads.end()!=thread_it; ++thread_it)
    {
        {
            MutexLock lock(&(*thread_it)->m_Mutex);
            (*thread_it)->m_Condition.SignalAll();
        }   // lock

        pthread_join((*thread_it)->m_ThreadId, NULL);
        delete *thread_it;
    }   // for

    // release any objects hanging in work queue
    for (work_it=m_WorkQueue.begin(); m_WorkQueue.end()!=work_it; ++work_it)
    {
        (*work_it)->RefDec();
    }   // for

    return;

}   // HotThreadPool::~HotThreadPool

bool                           // returns true if available worker thread found and claimed
HotThreadPool::FindWaitingThread(
    ThreadTask * work, // non-NULL to pass current work directly to a thread,
                       // NULL to potentially nudge an available worker toward backlog queue
    bool OkToQueue)
{
    bool ret_flag;
    size_t start, index, pool_size;

    ret_flag=false;

    // pick "random" place in thread list.  hopefully
    //  list size is prime number.
    pool_size=m_Threads.size();
    if (OkToQueue)
        start=(size_t)pthread_self() % pool_size;
    else
        start=0;
    index=start;

    do
    {
        // perform quick test to see thread available
        if (0!=m_Threads[index]->m_Available && !shutdown_pending())
        {
            // perform expensive compare and swap to potentially
            //  claim worker thread (this is an exclusive claim to the worker)
            ret_flag = compare_and_swap(&m_Threads[index]->m_Available, 1, 0);

            // the compare/swap only succeeds if worker thread is sitting on
            //  pthread_cond_wait ... or is about to be there but is holding
            //  the mutex already
            if (ret_flag)
            {

                // man page says mutex lock optional, experience in
                //  this code says it is not.  using broadcast instead
                //  of signal to cover one other race condition
                //  that should never happen with single thread waiting.
                MutexLock lock(&m_Threads[index]->m_Mutex);
                m_Threads[index]->m_DirectWork=work;
                m_Threads[index]->m_Condition.SignalAll();
            }   // if
        }   // if

        index=(index+1)%pool_size;

    } while(index!=start && !ret_flag && OkToQueue);

    return(ret_flag);

}   // FindWaitingThread

bool
HotThreadPool::Submit(
    ThreadTask* item,
    bool OkToQueue)
{
    bool ret_flag(false);
    int ret_val;

    if (NULL!=item)
    {
        item->RefInc();

        // do nothing if shutting down
        if(shutdown_pending())
        {
            item->RefDec();
            ret_flag=false;
        }   // if

        // try to give work to a waiting thread first
        else if (FindWaitingThread(item, OkToQueue))
        {
            IncWorkDirect();
            ret_flag=true;
        }   // else if

        else if (OkToQueue)
        {
            item->m_QueueStart=Env::Default()->NowMicros();

            // no waiting threads, put on backlog queue
            {
                SpinLock lock(&m_QueueLock);
                inc_and_fetch(&m_WorkQueueAtomic);
                m_WorkQueue.push_back(item);
            }

            // to address race condition, thread might be waiting now
            FindWaitingThread(NULL, true);

            // to address second race condition, send in QueueThread
            //   (thread not likely good on OSX)
            if (m_QueueThread.m_ThreadGood)
            {
                ret_val=sem_post(m_QueueThread.m_SemaphorePtr);
                if (0!=ret_val)
                {
                    syslog(LOG_ERR, "sem_post failed in HotThreadPool::Submit [%d, %m]",
                           errno);
                    gPerfCounters->Inc(ePerfThreadError);
                }   // if
            }   // if

            IncWorkQueued();
            ret_flag=true;
        }   // else if

        // did not post to thread or queue
        else
        {
            item->RefDec();
            ret_flag=false;  // redundant, but safe
        }   // else
    }   // if

    return(ret_flag);

}   // HotThreadPool::Submit

};  // namespace leveldb
<|endoftext|>


<commit_before>// ========================================================================================
//  ExecutionGraph
//  Copyright (C) 2014 by Gabriel Nützi <gnuetzi (at) gmail (døt) com>
//
//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================================

#ifndef ExecutionGraph_nodes_LogicSocket_hpp
#define ExecutionGraph_nodes_LogicSocket_hpp

#include <meta/meta.hpp>

#include "ExecutionGraph/common/Asserts.hpp"
#include "ExecutionGraph/common/DemangleTypes.hpp"
#include "ExecutionGraph/common/EnumClassHelper.hpp"
#include "ExecutionGraph/common/TypeDefs.hpp"
#include "ExecutionGraph/nodes/LogicCommon.hpp"

namespace executionGraph
{
//! The socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketBase
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    LogicSocketBase(IndexType type,
                    SocketIndex index,
                    NodeBaseType& parent,
                    const std::string& name = "noname")
        : m_type(type), m_index(index), m_parent(parent), m_name(name)
    {
    }

    IndexType getType() const { return m_type; }
    SocketIndex getIndex() const { return m_index; }
    std::string getName() const { return m_name; }

    const NodeBaseType& getParent() const { return m_parent; }
    NodeBaseType& getParent() { return m_parent; }

protected:
    const IndexType m_type;     //!< The index in to the meta::list SocketTypes, which type this is!
    const SocketIndex m_index;  //!< The index of the slot at which this socket is installed in a LogicNode.
    NodeBaseType& m_parent;     //!< The parent logic node of of this socket.
    const std::string m_name;   //!< The name of the socket.

    std::string getNameOfType() const;  //!< Returns the name of the current type.
};

template<typename TConfig>
std::string LogicSocketBase<TConfig>::getNameOfType() const
{
    std::string s = "'type-not-found'";
    IndexType i   = 0;
    auto f        = [&](auto type) {
        if (i == m_type)
        {
            s = demangle(type);
        }
        i++;
    };

    meta::for_each(SocketTypes{}, f);
    return s;
}

//! The input socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketInputBase : public LogicSocketBase<TConfig>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    friend class LogicSocketOutputBase<Config>;

    template<typename... Args>
    LogicSocketInputBase(Args&&... args)
        : LogicSocketBase<TConfig>(std::forward<Args>(args)...)
    {
    }

    //! Cast to a logic socket of type \p SocketInputType<T>*.
    //! The cast fails at runtime if the data type \p T does not match!
    template<typename T>
    inline auto* castToType() const
    {
        EXEC_GRAPH_THROW_BADSOCKETCAST_IF((this->m_type != meta::find_index<SocketTypes, T>::value),
                                          "Casting socket index '" << this->m_index << "' with type '" << this->getNameOfType() << "' into '"
                                                                   << demangle<T>()
                                                                   << "' of logic node id: '"
                                                                   << this->m_parent.getId()
                                                                   << "' which is wrong!");

        return static_cast<SocketInputType<T> const*>(this);
    }

    //! Non-const overload.
    template<typename T>
    inline auto* castToType()
    {
        return const_cast<SocketInputType<T>*>(static_cast<LogicSocketInputBase const*>(this)->castToType<T>());
    }

    void setGetLink(LogicSocketOutputBase<Config>& outputSocket);

    //! Check if the socket has a Get-Link to an output socket.
    inline bool hasGetLink() const { return m_from != nullptr; }

    inline LogicSocketOutputBase<Config>* getGetLink() { return m_from; }

protected:
    LogicSocketOutputBase<Config>* m_from = nullptr;  //!< The single Get-Link attached to this Socket.
};

//! The input socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketOutputBase : public LogicSocketBase<TConfig>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);
    friend class LogicSocketInputBase<Config>;

    template<typename... Args>
    LogicSocketOutputBase(Args&&... args)
        : LogicSocketBase<TConfig>(std::forward<Args>(args)...)
    {
    }

    //! Cast to a logic socket of type \p SocketOutputType<T>*.
    //! The cast fails at runtime (if NDEBUG defined) if the data type \p T does not match!
    template<typename T>
    inline auto* castToType() const
    {
        EXEC_GRAPH_THROW_BADSOCKETCAST_IF((this->m_type != meta::find_index<SocketTypes, T>::value),
                                          "Casting socket index '" << this->m_index << "' with type '" << this->getNameOfType() << "' into '"
                                                                   << demangle<T>()
                                                                   << "' of logic node id: '"
                                                                   << this->m_parent.getId()
                                                                   << "' which is wrong!");

        return static_cast<SocketOutputType<T> const*>(this);
    }

    //! Non-const overload.
    template<typename T>
    inline auto* castToType()
    {
        return const_cast<SocketOutputType<T>*>(static_cast<LogicSocketOutputBase const*>(this)->castToType<T>());
    }

    void addWriteLink(LogicSocketInputBase<Config>& inputSocket);

protected:
    std::vector<LogicSocketInputBase<Config>*> m_to;  //!< All Write-Links attached to this Socket.
};

template<typename TData>
class LogicSocketData
{
public:
    using DataType = TData;

    template<typename T>
    LogicSocketData(T&& defaultValue)
        : m_data(std::forward<T>(defaultValue))
    {
    }

    //! Get the data value of the socket.
    inline const DataType& getValue() const { return m_data; }
    //! Non-const overload.
    inline DataType& getValue() { return m_data; }

    //! Set the data value of the socket.
    template<typename T>
    void setValue(T&& value)
    {
        m_data = std::forward<T>(value);
    }

protected:
    DataType m_data;  //!< Default value! or the output value if output socket
};

template<typename TData, typename TConfig>
class LogicSocketInput : public LogicSocketInputBase<TConfig>,
                         public LogicSocketData<TData>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);
    using DataType = TData;

    /** This assert fails if the type T of the LogicSocket is
        not properly added to the type list SocketTypes in LogicSocketBase*/
    static_assert(!std::is_same<meta::find<SocketTypes, DataType>, meta::list<>>::value,
                  "TData is not in SocketTypes!");

    template<typename T, typename... Args>
    LogicSocketInput(T&& defaultValue, Args&&... args)
        : LogicSocketInputBase<TConfig>(meta::find_index<SocketTypes, DataType>::value, std::forward<Args>(args)...)
        , LogicSocketData<TData>(std::forward<T>(defaultValue))
    {
    }

    //! Get the data value of the socket. (follow Get-Link)
    inline const DataType& getValue() const
    {
        if (this->m_from != nullptr)
        {
            return static_cast<LogicSocketOutput<DataType, Config>*>(this->m_from)->getValue();
        }
        else
        {
            return this->m_data;
        }
    }
    //! Non-const overload.
    inline DataType& getValue()
    {
        return const_cast<DataType&>(static_cast<const LogicSocketInput*>(this)->getValue());
    }
};

template<typename TData, typename TConfig>
class LogicSocketOutput : public LogicSocketOutputBase<TConfig>,
                          public LogicSocketData<TData>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    using DataType = TData;
    /** This assert fails if the type T of the LogicSocket is
        not properly added to the type list SocketTypes in LogicSocketBase*/
    static_assert(!std::is_same<meta::find<SocketTypes, DataType>, meta::list<>>::value,
                  "TData is not in SocketTypes!");

    template<typename T, typename... Args>
    LogicSocketOutput(T&& defaultValue, Args&&... args)
        : LogicSocketOutputBase<TConfig>(meta::find_index<SocketTypes, DataType>::value, std::forward<Args>(args)...)
        , LogicSocketData<TData>(std::forward<T>(defaultValue))
    {
    }
    //! Set the data value of the socket.
    template<typename T>
    void setValue(T&& value)
    {
        // Set the value
        LogicSocketData<TData>::setValue(std::forward<T>(value));
        // Forward the value to all Write-Links
        executeWriteLinks();
    }

    void executeWriteLinks();
};

}  // end ExecutionGraph

// =====================================================================
// Implementation
// =====================================================================
namespace executionGraph
{
template<typename TConfig>
void LogicSocketOutputBase<TConfig>::addWriteLink(LogicSocketInputBase<TConfig>& inputSocket)
{
    EXEC_GRAPH_THROWEXCEPTION_TYPE_IF(this->getType() != inputSocket.getType(),
                                      "Output socket: " << this->getName() << " of logic node id: " << this->getParent().getId()
                                                        << " has not the same type as input socket "
                                                        << inputSocket.getName()
                                                        << " of logic node id: "
                                                        << inputSocket.getParent().getId(),
                                      NodeConnectionException);

    if (std::find(m_to.begin(), m_to.end(), &inputSocket) == m_to.end())
    {
        m_to.push_back(&inputSocket);
    }
    else
    {
        EXEC_GRAPH_THROWEXCEPTION_TYPE("Input socket: " << inputSocket.getName() << " of logic node id: "
                                                        << inputSocket.getParent().getId()
                                                        << " already added as Write-Link to logic node id: "
                                                        << this->getParent().getId(),
                                       NodeConnectionException);
    }
}

template<typename TConfig>
void LogicSocketInputBase<TConfig>::setGetLink(LogicSocketOutputBase<TConfig>& outputSocket)
{
    EXEC_GRAPH_THROWEXCEPTION_TYPE_IF(
        this->getType() != outputSocket.getType(),
        "Output socket: " << outputSocket.getName() << " of logic node id: " << outputSocket.getParent().getId()
                          << " has not the same type as input socket "
                          << this->getName()
                          << " of logic node id: "
                          << this->getParent().getId(),
        NodeConnectionException);

    if (!hasGetLink())
    {
        m_from = &outputSocket;
    }
    else
    {
        EXEC_GRAPH_THROWEXCEPTION_TYPE("Output socket: " << outputSocket.getName() << " of logic node id: "
                                                         << outputSocket.getParent().getId()
                                                         << " already set as Get-Link of logic node id: "
                                                         << this->getParent().getId(),
                                       NodeConnectionException);
    }
}

template<typename TData, typename TConfig>
void LogicSocketOutput<TData, TConfig>::executeWriteLinks()
{
    // Write out value to all connected (Write-Link) input sockets.
    for (auto& inputSocket : this->m_to)
    {
        // We know that this static cast is safe, since it has been
        // checked when addWriteLink() is called.
        static_cast<LogicSocketInput<TData, Config>*>(this->s)->setValue(this->m_data);  // Write data to input sockets.
    }
}

}  // end executionGraph

// =====================================================================
// Macros
// =====================================================================

#define ADD_ISOCK(name, value)                                                                        \
    GRSF_ASSERTMSG(Inputs::name == this->m_inputs.size(), " Wrong order for Input: " << Inputs::name) \
    addISock<IType##name>(value);

#define ADD_OSOCK(name, value)                                                                            \
    GRSF_ASSERTMSG(Outputs::name == this->m_outputs.size(), " Wrong order for Output: " << Outputs::name) \
    addOSock<OType##name>(value);

#define REMOVEPTR_REMOVEREF(_ptr_) std::remove_pointer<typename std::remove_reference<decltype(_ptr_)>::type>::type

/** VALUE SETTER MACROS */
#define SET_ISOCKET_VALUE_PTR(ptr, name, value)                                                                    \
    ptr->setISocketValue<typename REMOVEPTR_REMOVEREF(ptr)::IType##name>((REMOVEPTR_REMOVEREF(ptr)::Inputs::name), \
                                                                         value)

#define SET_OSOCKET_VALUE_PTR(ptr, name, value)                                                                     \
    ptr->setOSocketValue<typename REMOVEPTR_REMOVEREF(ptr)::OType##name>((REMOVEPTR_REMOVEREF(ptr)::Outputs::name), \
                                                                         value)

#define DISTRIBUTE_OSOCKET_VALUE_PTR(ptr, name)                                  \
    ptr->distributeOSocketValue<typename REMOVEPTR_REMOVEREF(ptr)::OType##name>( \
        (REMOVEPTR_REMOVEREF(ptr)::Outputs::name))

#define SET_ISOCKET_VALUE(name, value) SET_ISOCKET_VALUE_PTR(this, name, value)

#define SET_OSOCKET_VALUE(name, value) SET_OSOCKET_VALUE_PTR(this, name, value)

#define DISTRIBUTE_OSOCKET_VALUE(name) DISTRIBUTE_OSOCKET_VALUE_PTR(this, name)

/** VALUE GETTER MACROS */
#define GET_ISOCKET_VALUE_PTR(ptr, name) \
    ptr->getISocketValue<typename REMOVEPTR_REMOVEREF(ptr)::IType##name>((REMOVEPTR_REMOVEREF(ptr)::Inputs::name))

#define GET_OSOCKET_VALUE_PTR(ptr, name) \
    ptr->getOSocketValue<typename REMOVEPTR_REMOVEREF(ptr)::OType##name>((REMOVEPTR_REMOVEREF(ptr)::Outputs::name))

#define GET_ISOCKET_VALUE(name) GET_ISOCKET_VALUE_PTR(this, name)

#define GET_OSOCKET_VALUE(name) GET_OSOCKET_VALUE_PTR(this, name)

/** VALUE GETTER REFERENCE MACROS */
#define GET_ISOCKET_REF_VALUE_PTR(ptr, name) getISocketRefValue<IType##name>((Inputs::name))

#define GET_OSOCKET_REF_VALUE_PTR(ptr, name) getOSocketRefValue<OType##name>((Outputs::name))

#define GET_ISOCKET_REF_VALUE(name) GET_ISOCKET_REF_VALUE_PTR(this, name)

#define GET_OSOCKET_REF_VALUE(name) GET_OSOCKET_REF_VALUE_PTR(this, name)

/** SOCKET GETTER REFERENCE MACROS */
#define GET_ISOCKET_PTR(ptr, name) getISocket<IType##name>((Inputs::name))

#define GET_OSOCKET_PTR(ptr, name) getOSocket<OType##name>((Outputs::name))

#define GET_ISOCKET(name) GET_ISOCKET_PTR(this, name)

#define GET_OSOCKET(name) GET_OSOCKET_PTR(this, name)

/** SOCKET DECLARATION MACROS */
#define DECLARE_ISOCKET_TYPE(name, type) \
    typedef type IType##name;            \
    inline LogicSocket<type>* getIn_##name() { return getISocket<type>((Inputs::name)); }

#define DECLARE_OSOCKET_TYPE(name, type) \
    typedef type OType##name;            \
    inline LogicSocket<type>* getOut_##name() { return getOSocket<type>((Outputs::name)); }

#endif
<commit_msg>compiling executionGraph : integer node test passed!<commit_after>// ========================================================================================
//  ExecutionGraph
//  Copyright (C) 2014 by Gabriel Nützi <gnuetzi (at) gmail (døt) com>
//
//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================================

#ifndef ExecutionGraph_nodes_LogicSocket_hpp
#define ExecutionGraph_nodes_LogicSocket_hpp

#include <meta/meta.hpp>

#include "ExecutionGraph/common/Asserts.hpp"
#include "ExecutionGraph/common/DemangleTypes.hpp"
#include "ExecutionGraph/common/EnumClassHelper.hpp"
#include "ExecutionGraph/common/TypeDefs.hpp"
#include "ExecutionGraph/nodes/LogicCommon.hpp"

namespace executionGraph
{
//! The socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketBase
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    LogicSocketBase(IndexType type,
                    SocketIndex index,
                    NodeBaseType& parent,
                    const std::string& name = "noname")
        : m_type(type), m_index(index), m_parent(parent), m_name(name)
    {
    }

    IndexType getType() const { return m_type; }
    SocketIndex getIndex() const { return m_index; }
    std::string getName() const { return m_name; }

    const NodeBaseType& getParent() const { return m_parent; }
    NodeBaseType& getParent() { return m_parent; }

protected:
    const IndexType m_type;     //!< The index in to the meta::list SocketTypes, which type this is!
    const SocketIndex m_index;  //!< The index of the slot at which this socket is installed in a LogicNode.
    NodeBaseType& m_parent;     //!< The parent logic node of of this socket.
    const std::string m_name;   //!< The name of the socket.

    std::string getNameOfType() const;  //!< Returns the name of the current type.
};

template<typename TConfig>
std::string LogicSocketBase<TConfig>::getNameOfType() const
{
    std::string s = "'type-not-found'";
    IndexType i   = 0;
    auto f        = [&](auto type) {
        if (i == m_type)
        {
            s = demangle(type);
        }
        i++;
    };

    meta::for_each(SocketTypes{}, f);
    return s;
}

//! The input socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketInputBase : public LogicSocketBase<TConfig>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    friend class LogicSocketOutputBase<Config>;

    template<typename... Args>
    LogicSocketInputBase(Args&&... args)
        : LogicSocketBase<TConfig>(std::forward<Args>(args)...)
    {
    }

    //! Cast to a logic socket of type \p SocketInputType<T>*.
    //! The cast fails at runtime if the data type \p T does not match!
    template<typename T>
    inline auto* castToType() const
    {
        EXEC_GRAPH_THROW_BADSOCKETCAST_IF((this->m_type != meta::find_index<SocketTypes, T>::value),
                                          "Casting socket index '" << this->m_index << "' with type '" << this->getNameOfType() << "' into '"
                                                                   << demangle<T>()
                                                                   << "' of logic node id: '"
                                                                   << this->m_parent.getId()
                                                                   << "' which is wrong!");

        return static_cast<SocketInputType<T> const*>(this);
    }

    //! Non-const overload.
    template<typename T>
    inline auto* castToType()
    {
        return const_cast<SocketInputType<T>*>(static_cast<LogicSocketInputBase const*>(this)->castToType<T>());
    }

    void setGetLink(LogicSocketOutputBase<Config>& outputSocket);

    //! Check if the socket has a Get-Link to an output socket.
    inline bool hasGetLink() const { return m_from != nullptr; }

    inline LogicSocketOutputBase<Config>* getGetLink() { return m_from; }

protected:
    LogicSocketOutputBase<Config>* m_from = nullptr;  //!< The single Get-Link attached to this Socket.
};

//! The input socket base class for all input/output sockets of a logic node.
template<typename TConfig>
class LogicSocketOutputBase : public LogicSocketBase<TConfig>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);
    friend class LogicSocketInputBase<Config>;

    template<typename... Args>
    LogicSocketOutputBase(Args&&... args)
        : LogicSocketBase<TConfig>(std::forward<Args>(args)...)
    {
    }

    //! Cast to a logic socket of type \p SocketOutputType<T>*.
    //! The cast fails at runtime (if NDEBUG defined) if the data type \p T does not match!
    template<typename T>
    inline auto* castToType() const
    {
        EXEC_GRAPH_THROW_BADSOCKETCAST_IF((this->m_type != meta::find_index<SocketTypes, T>::value),
                                          "Casting socket index '" << this->m_index << "' with type '" << this->getNameOfType() << "' into '"
                                                                   << demangle<T>()
                                                                   << "' of logic node id: '"
                                                                   << this->m_parent.getId()
                                                                   << "' which is wrong!");

        return static_cast<SocketOutputType<T> const*>(this);
    }

    //! Non-const overload.
    template<typename T>
    inline auto* castToType()
    {
        return const_cast<SocketOutputType<T>*>(static_cast<LogicSocketOutputBase const*>(this)->castToType<T>());
    }

    void addWriteLink(LogicSocketInputBase<Config>& inputSocket);

protected:
    std::vector<LogicSocketInputBase<Config>*> m_to;  //!< All Write-Links attached to this Socket.
};

template<typename TData>
class LogicSocketData
{
public:
    using DataType = TData;

    template<typename T>
    LogicSocketData(T&& defaultValue)
        : m_data(std::forward<T>(defaultValue))
    {
    }

    //! Get the data value of the socket.
    inline const DataType& getValue() const { return m_data; }
    //! Non-const overload.
    inline DataType& getValue() { return m_data; }

    //! Set the data value of the socket.
    template<typename T>
    void setValue(T&& value)
    {
        m_data = std::forward<T>(value);
    }

protected:
    DataType m_data;  //!< Default value! or the output value if output socket
};

template<typename TData, typename TConfig>
class LogicSocketInput : public LogicSocketInputBase<TConfig>,
                         public LogicSocketData<TData>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);
    using DataType = TData;

    /** This assert fails if the type T of the LogicSocket is
        not properly added to the type list SocketTypes in LogicSocketBase*/
    static_assert(!std::is_same<meta::find<SocketTypes, DataType>, meta::list<>>::value,
                  "TData is not in SocketTypes!");

    template<typename T, typename... Args>
    LogicSocketInput(T&& defaultValue, Args&&... args)
        : LogicSocketInputBase<TConfig>(meta::find_index<SocketTypes, DataType>::value, std::forward<Args>(args)...)
        , LogicSocketData<TData>(std::forward<T>(defaultValue))
    {
    }

    //! Get the data value of the socket. (follow Get-Link)
    inline const DataType& getValue() const
    {
        if (this->m_from != nullptr)
        {
            return static_cast<LogicSocketOutput<DataType, Config>*>(this->m_from)->getValue();
        }
        else
        {
            return this->m_data;
        }
    }
    //! Non-const overload.
    inline DataType& getValue()
    {
        return const_cast<DataType&>(static_cast<const LogicSocketInput*>(this)->getValue());
    }
};

template<typename TData, typename TConfig>
class LogicSocketOutput : public LogicSocketOutputBase<TConfig>,
                          public LogicSocketData<TData>
{
public:
    EXEC_GRAPH_TYPEDEF_CONFIG(TConfig);

    using DataType = TData;
    /** This assert fails if the type T of the LogicSocket is
        not properly added to the type list SocketTypes in LogicSocketBase*/
    static_assert(!std::is_same<meta::find<SocketTypes, DataType>, meta::list<>>::value,
                  "TData is not in SocketTypes!");

    template<typename T, typename... Args>
    LogicSocketOutput(T&& defaultValue, Args&&... args)
        : LogicSocketOutputBase<TConfig>(meta::find_index<SocketTypes, DataType>::value, std::forward<Args>(args)...)
        , LogicSocketData<TData>(std::forward<T>(defaultValue))
    {
    }
    //! Set the data value of the socket.
    template<typename T>
    void setValue(T&& value)
    {
        // Set the value
        LogicSocketData<TData>::setValue(std::forward<T>(value));
        // Forward the value to all Write-Links
        executeWriteLinks();
    }

    void executeWriteLinks();
};

}  // end ExecutionGraph

// =====================================================================
// Implementation
// =====================================================================
namespace executionGraph
{
template<typename TConfig>
void LogicSocketOutputBase<TConfig>::addWriteLink(LogicSocketInputBase<TConfig>& inputSocket)
{
    EXEC_GRAPH_THROWEXCEPTION_TYPE_IF(this->getType() != inputSocket.getType(),
                                      "Output socket: " << this->getName() << " of logic node id: " << this->getParent().getId()
                                                        << " has not the same type as input socket "
                                                        << inputSocket.getName()
                                                        << " of logic node id: "
                                                        << inputSocket.getParent().getId(),
                                      NodeConnectionException);

    if (std::find(m_to.begin(), m_to.end(), &inputSocket) == m_to.end())
    {
        m_to.push_back(&inputSocket);
    }
    else
    {
        EXEC_GRAPH_THROWEXCEPTION_TYPE("Input socket: " << inputSocket.getName() << " of logic node id: "
                                                        << inputSocket.getParent().getId()
                                                        << " already added as Write-Link to logic node id: "
                                                        << this->getParent().getId(),
                                       NodeConnectionException);
    }
}

template<typename TConfig>
void LogicSocketInputBase<TConfig>::setGetLink(LogicSocketOutputBase<TConfig>& outputSocket)
{
    EXEC_GRAPH_THROWEXCEPTION_TYPE_IF(
        this->getType() != outputSocket.getType(),
        "Output socket: " << outputSocket.getName() << " of logic node id: " << outputSocket.getParent().getId()
                          << " has not the same type as input socket "
                          << this->getName()
                          << " of logic node id: "
                          << this->getParent().getId(),
        NodeConnectionException);

    if (!hasGetLink())
    {
        m_from = &outputSocket;
    }
    else
    {
        EXEC_GRAPH_THROWEXCEPTION_TYPE("Output socket: " << outputSocket.getName() << " of logic node id: "
                                                         << outputSocket.getParent().getId()
                                                         << " already set as Get-Link of logic node id: "
                                                         << this->getParent().getId(),
                                       NodeConnectionException);
    }
}

template<typename TData, typename TConfig>
void LogicSocketOutput<TData, TConfig>::executeWriteLinks()
{
    // Write out value to all connected (Write-Link) input sockets.
    for (auto& inputSocket : this->m_to)
    {
        // We know that this static cast is safe, since it has been
        // checked when addWriteLink() is called.
        static_cast<LogicSocketInput<TData, Config>*>(this->s)->setValue(this->m_data);  // Write data to input sockets.
    }
}

}  // end executionGraph
#endif
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (testabilitydriver@nokia.com)
**
** This file is part of Testability Driver Qt Agent
**
** If you have questions regarding the use of this file, please contact
** Nokia at testabilitydriver@nokia.com .
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QDebug>
#include <QFile>
#include "tascommandparser.h"
#include "taslogger.h"

/*!
    \class TasCommandParser
    \brief TasCommandParser parses xml in tas command format and returns an object model of it.

    Test framework sends ui commands to SUTs in an xml format. TasCommandParser component reads
    and parses the xml to an object model. The object model is than passed on to command handlers.

    The format is very simple. Below is an example from mouse click command on a GraphicsItem type
    object. The type attributein the target defines the type of the object. QGraphicsItem type object
    are of type "graphicsView" and normal QWidgets are of type "standardView".

    <TasCommands id="4320" service="uiState" async="true">
        <Target TasId="102859040" type="graphicsView">
            <Command name="MouseClick" button="1" delay="0">
            </Command>
        </Target>
    </TasCommands>

    The server details are used to send the responses (ui state) back to the test framework using
    tcp communication. The normal scenario is that the commanding ui does not results in
    ui states being send and the test framework asks for the separately.

*/

/*!

   Returns a TasCommandModel containing all of the details from the command xml.
   In cases where the xml cannot be parsed a null is returned so always check the
   model before using it.

*/
TasCommandModel* TasCommandParser::parseCommandXml(const QString& commandXml)
{
    TasCommandModel* model = 0;
    QDomDocument doc("TasCommands");
    QString errorMsg;
    if (doc.setContent(commandXml, &errorMsg)){
        model = new TasCommandModel();
        model->setSourceString(commandXml);
        QDomElement root = doc.documentElement();
        model->setId(root.attribute("id"));
        model->setUId(root.attribute("uid"));
        model->setName(root.attribute("name"));
        model->setService(root.attribute("service"));
        if(root.attribute("async") == "true"){
            model->setAsynchronous(true);
        }
        if(!root.attribute("interval").isEmpty()){
            model->setInterval(root.attribute("interval").toInt());
        }

        if(root.attribute("multitouch") == "true"){
            model->setMultitouch(true);
        }

        //get targets
        QDomNodeList targets = doc.elementsByTagName (QString("Target"));
        int count = targets.count();
        for (int i = 0; i < count; i++){
            QDomElement target = targets.item(i).toElement();
            QString targetId = target.attribute("TasId");
            QString targetType = target.attribute("type");
            TasTarget& targetData = model->addTarget(targetId);
            targetData.setType(targetType);

            //get commands for target
            QDomNodeList commands = target.elementsByTagName(QString("Command"));
            int commandCount = commands.count();
            for(int j = 0 ; j < commandCount; j++){
                TasCommand& commandData = targetData.addCommand();
                QDomElement command = commands.item(j).toElement();
                QDomNamedNodeMap attributes = command.attributes();
                int attributeCount = attributes.count();
                for(int k = 0 ; k < attributeCount; k++){
                    QDomNode node = attributes.item(k);
                    QString name = node.nodeName();
                    QString value = node.nodeValue();
                    if(name == "name"){
                        commandData.setName(value);
                    }
                    else{
                        commandData.addParameter(name, value);
                    }
                }
                QDomNodeList apiParams = target.elementsByTagName(QString("param"));
                int apiParamCount = apiParams.count();
                for(int k = 0 ; k < apiParamCount; k++){
                    QDomElement apiParam = apiParams.item(k).toElement();
                    QString paramName = apiParam.attribute("name");
                    QString paramValue = apiParam.attribute("value");
                    QString paramType = apiParam.attribute("type");
                    commandData.addApiParameter(paramName, paramValue,paramType);
                }

                commandData.setText(command.text());
            }
        }
    }
    else{
        TasLogger::logger()->error("TasCommandParser::parseCommandXml Could not parse the xml. Reason: " + errorMsg);
        //TasLogger::logger()->error("[" + commandXml + "]");
    }
    doc.clear();
    return model;
}

<commit_msg>Application uid parsed from command xml<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (testabilitydriver@nokia.com)
**
** This file is part of Testability Driver Qt Agent
**
** If you have questions regarding the use of this file, please contact
** Nokia at testabilitydriver@nokia.com .
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QDebug>
#include <QFile>
#include "tascommandparser.h"
#include "taslogger.h"

/*!
    \class TasCommandParser
    \brief TasCommandParser parses xml in tas command format and returns an object model of it.

    Test framework sends ui commands to SUTs in an xml format. TasCommandParser component reads
    and parses the xml to an object model. The object model is than passed on to command handlers.

    The format is very simple. Below is an example from mouse click command on a GraphicsItem type
    object. The type attributein the target defines the type of the object. QGraphicsItem type object
    are of type "graphicsView" and normal QWidgets are of type "standardView".

    <TasCommands id="4320" service="uiState" async="true">
        <Target TasId="102859040" type="graphicsView">
            <Command name="MouseClick" button="1" delay="0">
            </Command>
        </Target>
    </TasCommands>

    The server details are used to send the responses (ui state) back to the test framework using
    tcp communication. The normal scenario is that the commanding ui does not results in
    ui states being send and the test framework asks for the separately.

*/

/*!

   Returns a TasCommandModel containing all of the details from the command xml.
   In cases where the xml cannot be parsed a null is returned so always check the
   model before using it.

*/
TasCommandModel* TasCommandParser::parseCommandXml(const QString& commandXml)
{
    TasCommandModel* model = 0;
    QDomDocument doc("TasCommands");
    QString errorMsg;
    if (doc.setContent(commandXml, &errorMsg)){
        model = new TasCommandModel();
        model->setSourceString(commandXml);
        QDomElement root = doc.documentElement();
        model->setId(root.attribute("id"));
        model->setUId(root.attribute("applicationUid"));
        model->setName(root.attribute("name"));
        model->setService(root.attribute("service"));
        if(root.attribute("async") == "true"){
            model->setAsynchronous(true);
        }
        if(!root.attribute("interval").isEmpty()){
            model->setInterval(root.attribute("interval").toInt());
        }

        if(root.attribute("multitouch") == "true"){
            model->setMultitouch(true);
        }

        //get targets
        QDomNodeList targets = doc.elementsByTagName (QString("Target"));
        int count = targets.count();
        for (int i = 0; i < count; i++){
            QDomElement target = targets.item(i).toElement();
            QString targetId = target.attribute("TasId");
            QString targetType = target.attribute("type");
            TasTarget& targetData = model->addTarget(targetId);
            targetData.setType(targetType);

            //get commands for target
            QDomNodeList commands = target.elementsByTagName(QString("Command"));
            int commandCount = commands.count();
            for(int j = 0 ; j < commandCount; j++){
                TasCommand& commandData = targetData.addCommand();
                QDomElement command = commands.item(j).toElement();
                QDomNamedNodeMap attributes = command.attributes();
                int attributeCount = attributes.count();
                for(int k = 0 ; k < attributeCount; k++){
                    QDomNode node = attributes.item(k);
                    QString name = node.nodeName();
                    QString value = node.nodeValue();
                    if(name == "name"){
                        commandData.setName(value);
                    }
                    else{
                        commandData.addParameter(name, value);
                    }
                }
                QDomNodeList apiParams = target.elementsByTagName(QString("param"));
                int apiParamCount = apiParams.count();
                for(int k = 0 ; k < apiParamCount; k++){
                    QDomElement apiParam = apiParams.item(k).toElement();
                    QString paramName = apiParam.attribute("name");
                    QString paramValue = apiParam.attribute("value");
                    QString paramType = apiParam.attribute("type");
                    commandData.addApiParameter(paramName, paramValue,paramType);
                }

                commandData.setText(command.text());
            }
        }
    }
    else{
        TasLogger::logger()->error("TasCommandParser::parseCommandXml Could not parse the xml. Reason: " + errorMsg);
        //TasLogger::logger()->error("[" + commandXml + "]");
    }
    doc.clear();
    return model;
}

<|endoftext|>


<commit_before>#include <Refal2.h>

namespace Refal2 {

//-----------------------------------------------------------------------------
// CProgram

CProgram::CProgram( int numberOfModules ) :
	modulesSize( numberOfModules ),
	modules( new CRuntimeModule[modulesSize] )
{
	assert( modulesSize > 0 );
	assert( modules != nullptr );
}

CProgram::~CProgram()
{
	delete[] modules;
}

CRuntimeModule& CProgram::Module( TRuntimeModuleId moduleId )
{
	assert( moduleId >= 0 && moduleId < modulesSize );
	return modules[moduleId];
}

//-----------------------------------------------------------------------------
// CProgramPrintHelper

void CProgramPrintHelper::Label( std::ostream& outputStream,
	const TLabel label ) const
{
	const int labelModuleId = label / LabelMask;
	if( PrintLabelWithModule() ) {
		outputStream << labelModuleId << ":";
	}
	outputStream << program->Module( labelModuleId ).Functions.
		GetKey(	label % LabelMask );
}

//-----------------------------------------------------------------------------
// Standart embedded functions

static void notImplemented( const char* name )
{
	std::cout << "external function `" << name
		<< "` not implemented yet." << std::endl;
}

static bool embeddedPrint( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.StrangePrint( std::cout, CPrintHelper() );
	return true;
}

static bool embeddedPrintm( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.HandyPrint( std::cout, CPrintHelper() );
	return true;
}

static bool embeddedProut( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.StrangePrint( std::cout, CPrintHelper() );
	executionContext.Argument.Empty();
	return true;
}

static bool embeddedProutm( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.HandyPrint( std::cout, CPrintHelper() );
	executionContext.Argument.Empty();
	return true;
}

static bool embeddedChartof( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	std::string labelText;
	for( CUnitNode* c = executionContext.Argument.GetFirst(); c != 0;
		c = c->Next() )
	{
		if( !c->IsChar() ) {
			return false;
		}
		labelText += c->Char();
	}
	CRuntimeFunctions& functions = executionContext.Program->Module(
		executionContext.RuntimeModuleId ).Functions;
	const int labelId = functions.FindKey( labelText );
	if( labelId == InvalidDictionaryIndex ) {
		return false;
	}
	executionContext.Argument.Empty();
	executionContext.Argument.AppendLabel( labelId
		+ LabelMask * executionContext.RuntimeModuleId );
	return true;
}

static bool embeddedFtochar( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	const CUnit* label = executionContext.Argument.GetFirst();
	if( !label->IsLabel() ) {
		return false;
	}
	std::ostringstream stringStream;
	CProgramPrintHelper( executionContext.Program ).Label( stringStream,
		label->Label() );
	std::string labelText = stringStream.str();
	executionContext.Argument.Empty();
	for( std::string::const_iterator c = labelText.begin();
		c != labelText.end(); ++c )
	{
		executionContext.Argument.AppendChar( *c );
	}
	return true;
}

static bool embeddedFunctab( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.Empty();
	return true;
}

//-----------------------------------------------------------------------------
// CStandartEmbeddedFunctionData

struct CStandartEmbeddedFunctionData {
	const char* const externalName;
	const TEmbeddedFunctionPtr EmbeddedFunction;
};

const CStandartEmbeddedFunctionData standartEmbeddedFunctions[] = {
	{ "print", embeddedPrint },
	{ "printm", embeddedPrintm },
	{ "prout", embeddedProut },
	{ "proutm", embeddedProutm },
	{ "chartof", embeddedChartof },
	{ "ftochar", embeddedFtochar },
	{ "functab", embeddedFunctab },
	{ nullptr, nullptr }
};

//-----------------------------------------------------------------------------
// CGlobalFunctionData

class CGlobalFunctionData {
public:
	CGlobalFunctionData();

	static CRuntimeFunctionPtr EmptyFunction();

	bool IsEmbeddedFunction() const;
	bool IsPreparatoryFunction() const;
	bool IsDefined() const;

	void SetPreparatoryFunction(
		const CPreparatoryFunction* const _preparatoryFunction,
		const TRuntimeModuleId _runtimeModuleId );
	const CPreparatoryFunction* PreparatoryFunction() const;

	const TRuntimeModuleId RuntimeModuleId() const;

	void SetEmbeddedFunction( const TEmbeddedFunctionPtr _embeddedFunction );
	TEmbeddedFunctionPtr EmbeddedFunction() const;

	CRuntimeFunctionPtr RuntimeFunction() const;

private:
	const CPreparatoryFunction* preparatoryFunction;
	TEmbeddedFunctionPtr embeddedFunction;
	TRuntimeModuleId runtimeModuleId;
	mutable CRuntimeFunctionPtr runtimeFunction;
};

//-----------------------------------------------------------------------------

CGlobalFunctionData::CGlobalFunctionData() :
	preparatoryFunction( nullptr ),
	embeddedFunction( nullptr ),
	runtimeModuleId( 0 )
{
}

CRuntimeFunctionPtr CGlobalFunctionData::EmptyFunction()
{
	static const CRuntimeFunctionPtr emptyFunction( new CEmptyFunction );
	return emptyFunction;
}

bool CGlobalFunctionData::IsEmbeddedFunction() const
{
	return ( embeddedFunction != nullptr );
}

bool CGlobalFunctionData::IsPreparatoryFunction() const
{
	return ( preparatoryFunction != nullptr );
}

bool CGlobalFunctionData::IsDefined() const
{
	return ( IsEmbeddedFunction() || IsPreparatoryFunction() );
}

void CGlobalFunctionData::SetPreparatoryFunction(
	const CPreparatoryFunction* const _preparatoryFunction,
	const TRuntimeModuleId _runtimeModuleId )
{
	assert( !IsDefined() );
	assert( _preparatoryFunction != nullptr );
	preparatoryFunction = _preparatoryFunction;
	runtimeModuleId = _runtimeModuleId;
}

const CPreparatoryFunction* CGlobalFunctionData::PreparatoryFunction() const
{
	assert( IsPreparatoryFunction() );
	return preparatoryFunction;
}

const TRuntimeModuleId CGlobalFunctionData::RuntimeModuleId() const
{
	assert( IsPreparatoryFunction() );
	return runtimeModuleId;
}

void CGlobalFunctionData::SetEmbeddedFunction(
	const TEmbeddedFunctionPtr _embeddedFunction )
{
	assert( !IsDefined() );
	assert( _embeddedFunction != nullptr );
	embeddedFunction = _embeddedFunction;
}

TEmbeddedFunctionPtr CGlobalFunctionData::EmbeddedFunction() const
{
	assert( IsEmbeddedFunction() );
	return embeddedFunction;
}

CRuntimeFunctionPtr CGlobalFunctionData::RuntimeFunction() const
{
	if( !static_cast<bool>( runtimeFunction ) ) {
		if( IsEmbeddedFunction() ) {
			runtimeFunction.reset( new CEmbeddedFunction(
				embeddedFunction ) );
		} else {
			assert( IsPreparatoryFunction() );
			if( PreparatoryFunction()->IsEmpty() ) {
				runtimeFunction = EmptyFunction();
			} else {
				runtimeFunction.reset( new COrdinaryFunction(
					PreparatoryFunction()->FirstOperation(),
					RuntimeModuleId() ) );
			}
		}
	}
	return runtimeFunction;
}

//-----------------------------------------------------------------------------
// CInternalProgramBuilder

const char* const ProgramStartFunctionName = "go";

class CInternalProgramBuilder {
public:
	static CProgramPtr Build( CModuleDataVector& modules,
		CErrorsHelper& errors );

private:
	CErrorsHelper& errors;
	CDictionary<CGlobalFunctionData, std::string> globals;
	CProgramPtr program;

	CInternalProgramBuilder( CErrorsHelper& _errors, int numberOfModules );

	typedef void ( CInternalProgramBuilder::*TProcessFunctionPtr )(
		CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );

	void processModules( const CModuleDataVector& modules,
		const TProcessFunctionPtr processFunction );

	void collectFunction( CPreparatoryFunction* function,
		 const TRuntimeModuleId runtimeModuleId );
	void collect( const CModuleDataVector& modules );

	void check();

	void compileFunction( CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );
	void compile( const CModuleDataVector& modules );

	void linkFunction( CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );
	void link( const CModuleDataVector& modules );
};

//-----------------------------------------------------------------------------

CInternalProgramBuilder::CInternalProgramBuilder( CErrorsHelper& _errors,
		int numberOfModules ) :
	errors( _errors ),
	program( new CProgram( numberOfModules ) )
{
	assert( static_cast<bool>( program ) );
	// add standart functions
	for( int i = 0; standartEmbeddedFunctions[i].EmbeddedFunction != nullptr;
		i++ )
	{
		assert( standartEmbeddedFunctions[i].externalName != nullptr );
		std::string externalName = standartEmbeddedFunctions[i].externalName;
		assert( !externalName.empty() );
		const int globalIndex = globals.AddKey( externalName );
		CGlobalFunctionData& global = globals.GetData( globalIndex );
		global.SetEmbeddedFunction(
			standartEmbeddedFunctions[i].EmbeddedFunction );
	}
	// add `ProgramStartFunctionName`
	assert( !globals.GetData( globals.AddKey(
		ProgramStartFunctionName ) ).IsDefined() );
}

CProgramPtr CInternalProgramBuilder::Build( CModuleDataVector& modules,
	CErrorsHelper& errors )
{
	assert( !errors.HasErrors() );
	assert( !modules.empty() );
	CInternalProgramBuilder builder( errors, modules.size() );
	builder.collect( modules );
	if( !errors.HasErrors() ) {
		builder.check();
		if( !errors.HasErrors() ) {
			builder.compile( modules );
			assert( !errors.HasErrors() );
			builder.link( modules );
			assert( !errors.HasErrors() );
			return builder.program;
		}
	}
	modules.clear();
	return nullptr;
}

void CInternalProgramBuilder::processModules( const CModuleDataVector& modules,
	const TProcessFunctionPtr processFunction )
{
	TRuntimeModuleId currentModuleId = 0;
	for( CModuleDataVector::const_iterator module = modules.begin();
		module != modules.end(); ++module )
	{
		const CPreparatoryFunctions& functions = ( *module )->Functions;
		for( int i = 0; i < functions.Size(); i++ ) {
			( this->*processFunction )( functions.GetData( i ).get(),
				currentModuleId );
		}
		currentModuleId++;
	}
}

void CInternalProgramBuilder::collectFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId runtimeModuleId )
{
	if( !function->IsExternal() && !function->IsEntry() ) {
		return;
	}

	const int globalIndex = globals.AddKey( function->ExternalName() );
	if( function->IsEntry() ) {
		assert( !function->IsExternal() );
		CGlobalFunctionData& global = globals.GetData( globalIndex );
		if( global.IsDefined() ) {
			std::ostringstream stringStream;
			stringStream << "function with external name `"
				<< function->ExternalNameToken().word
				<< "` already defined in program";
			errors.Error( stringStream.str() );
		} else {
			global.SetPreparatoryFunction( function, runtimeModuleId );
		}
	}
}

void CInternalProgramBuilder::collect( const CModuleDataVector& modules )
{
	processModules( modules, &CInternalProgramBuilder::collectFunction );
}

void CInternalProgramBuilder::check()
{
	for( int globalIndex = 0; globalIndex < globals.Size(); globalIndex++ )
	{
		if( !globals.GetData( globalIndex ).IsDefined() ) {
			std::ostringstream stringStream;
			stringStream << "function with external name `"
				<< globals.GetKey( globalIndex ) << "` was not defined";
			errors.Error( stringStream.str() );
		}
	}
}

void CInternalProgramBuilder::compileFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId /*runtimeModuleId*/ )
{
	CFunctionCompiler compiler( program->OperationsHolder() );
	if( function->IsOrdinary() ) {
		function->Compile( compiler );
	}
}

void CInternalProgramBuilder::compile( const CModuleDataVector& modules )
{
	processModules( modules, &CInternalProgramBuilder::compileFunction );
}

void CInternalProgramBuilder::linkFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId runtimeModuleId )
{
	CRuntimeFunctions& functions = program->Module( runtimeModuleId ).Functions;
	const int functionKey = functions.AddKey( function->Name() );
	CRuntimeFunctionPtr& runtimeFunction = functions.GetData( functionKey );
	assert( !static_cast<bool>( runtimeFunction ) );

	switch( function->GetType() ) {
		case PFT_Empty:
			runtimeFunction = CGlobalFunctionData::EmptyFunction();
			break;
		case PFT_Compiled:
			runtimeFunction.reset( new COrdinaryFunction(
				function->FirstOperation(), runtimeModuleId ) );
			break;
		case PFT_External:
		{
			const int globalIndex = globals.FindKey( function->ExternalName() );
			runtimeFunction = globals.GetData( globalIndex ).RuntimeFunction();
			break;
		}
		default:
			assert( false );
			break;
	}
}

void CInternalProgramBuilder::link( const CModuleDataVector& modules )
{
	const CGlobalFunctionData& goFunction = globals.GetData( globals.FindKey(
		ProgramStartFunctionName ) );
	TOperationAddress goFirstOperation =
		goFunction.PreparatoryFunction()->FirstOperation();
	TRuntimeModuleId goRuntimeModuleId = goFunction.RuntimeModuleId();
	// link all
	processModules( modules, &CInternalProgramBuilder::linkFunction );
	// set program start function
	const CRuntimeFunctions& runtimeFunctions =
		program->Module( goRuntimeModuleId ).Functions;
	TLabel goLabel = InvalidDictionaryIndex;
	for( int i = 0; i < runtimeFunctions.Size(); i++ ) {
		if( runtimeFunctions.GetData( i )->IsOrdinary()
			&& static_cast<const COrdinaryFunction*>(
				runtimeFunctions.GetData( i ).get() )->FirstOperation() ==
				goFirstOperation )
		{
			goLabel = i;
		}
	}
	assert( goLabel != InvalidDictionaryIndex );
	program->SetProgramStartFunction( goLabel + goRuntimeModuleId * LabelMask );
}

//-----------------------------------------------------------------------------
// CProgramBuilder

CProgramBuilder::CProgramBuilder( IErrorHandler* errorHandler ) :
	CFunctionBuilder( errorHandler )
{
	Reset();
}

void CProgramBuilder::Reset()
{
	CFunctionBuilder::Reset();
}

void CProgramBuilder::AddModule( CModuleDataPtr& module )
{
	modules.push_back( CModuleDataPtr( module.release() ) );
}

CProgramPtr CProgramBuilder::BuildProgram()
{
	return CInternalProgramBuilder::Build( modules, *this );
}

//-----------------------------------------------------------------------------

} // end of namespace Refal2
<commit_msg>error fix<commit_after>#include <Refal2.h>

namespace Refal2 {

//-----------------------------------------------------------------------------
// CProgram

CProgram::CProgram( int numberOfModules ) :
	modulesSize( numberOfModules ),
	modules( new CRuntimeModule[modulesSize] )
{
	assert( modulesSize > 0 );
	assert( modules != nullptr );
}

CProgram::~CProgram()
{
	delete[] modules;
}

CRuntimeModule& CProgram::Module( TRuntimeModuleId moduleId )
{
	assert( moduleId >= 0 && moduleId < modulesSize );
	return modules[moduleId];
}

//-----------------------------------------------------------------------------
// CProgramPrintHelper

void CProgramPrintHelper::Label( std::ostream& outputStream,
	const TLabel label ) const
{
	const int labelModuleId = label / LabelMask;
	if( PrintLabelWithModule() ) {
		outputStream << labelModuleId << ":";
	}
	outputStream << program->Module( labelModuleId ).Functions.
		GetKey(	label % LabelMask );
}

//-----------------------------------------------------------------------------
// Standart embedded functions

static void notImplemented( const char* name )
{
	std::cout << "external function `" << name
		<< "` not implemented yet." << std::endl;
}

static bool embeddedPrint( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.StrangePrint( std::cout, CPrintHelper() );
	return true;
}

static bool embeddedPrintm( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.HandyPrint( std::cout, CPrintHelper() );
	return true;
}

static bool embeddedProut( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.StrangePrint( std::cout, CPrintHelper() );
	executionContext.Argument.Empty();
	return true;
}

static bool embeddedProutm( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.HandyPrint( std::cout, CPrintHelper() );
	executionContext.Argument.Empty();
	return true;
}

static bool embeddedChartof( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	std::string labelText;
	for( CUnitNode* c = executionContext.Argument.GetFirst(); c != 0;
		c = c->Next() )
	{
		if( !c->IsChar() ) {
			return false;
		}
		labelText += c->Char();
	}
	CRuntimeFunctions& functions = executionContext.Program->Module(
		executionContext.RuntimeModuleId ).Functions;
	const int labelId = functions.FindKey( labelText );
	if( labelId == InvalidDictionaryIndex ) {
		return false;
	}
	executionContext.Argument.Empty();
	executionContext.Argument.AppendLabel( labelId
		+ LabelMask * executionContext.RuntimeModuleId );
	return true;
}

static bool embeddedFtochar( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	const CUnit* label = executionContext.Argument.GetFirst();
	if( !label->IsLabel() ) {
		return false;
	}
	std::ostringstream stringStream;
	CProgramPrintHelper( executionContext.Program ).Label( stringStream,
		label->Label() );
	std::string labelText = stringStream.str();
	executionContext.Argument.Empty();
	for( std::string::const_iterator c = labelText.begin();
		c != labelText.end(); ++c )
	{
		executionContext.Argument.AppendChar( *c );
	}
	return true;
}

static bool embeddedFunctab( CExecutionContext& executionContext )
{
	DEBUG_PRINT( __FUNCTION__ )
	executionContext.Argument.Empty();
	return true;
}

//-----------------------------------------------------------------------------
// CStandartEmbeddedFunctionData

struct CStandartEmbeddedFunctionData {
	const char* const externalName;
	const TEmbeddedFunctionPtr EmbeddedFunction;
};

const CStandartEmbeddedFunctionData standartEmbeddedFunctions[] = {
	{ "print", embeddedPrint },
	{ "printm", embeddedPrintm },
	{ "prout", embeddedProut },
	{ "proutm", embeddedProutm },
	{ "chartof", embeddedChartof },
	{ "ftochar", embeddedFtochar },
	{ "functab", embeddedFunctab },
	{ nullptr, nullptr }
};

//-----------------------------------------------------------------------------
// CGlobalFunctionData

class CGlobalFunctionData {
public:
	CGlobalFunctionData();

	static CRuntimeFunctionPtr EmptyFunction();

	bool IsEmbeddedFunction() const;
	bool IsPreparatoryFunction() const;
	bool IsDefined() const;

	void SetPreparatoryFunction(
		const CPreparatoryFunction* const _preparatoryFunction,
		const TRuntimeModuleId _runtimeModuleId );
	const CPreparatoryFunction* PreparatoryFunction() const;

	const TRuntimeModuleId RuntimeModuleId() const;

	void SetEmbeddedFunction( const TEmbeddedFunctionPtr _embeddedFunction );
	TEmbeddedFunctionPtr EmbeddedFunction() const;

	CRuntimeFunctionPtr RuntimeFunction() const;

private:
	const CPreparatoryFunction* preparatoryFunction;
	TEmbeddedFunctionPtr embeddedFunction;
	TRuntimeModuleId runtimeModuleId;
	mutable CRuntimeFunctionPtr runtimeFunction;
};

//-----------------------------------------------------------------------------

CGlobalFunctionData::CGlobalFunctionData() :
	preparatoryFunction( nullptr ),
	embeddedFunction( nullptr ),
	runtimeModuleId( 0 )
{
}

CRuntimeFunctionPtr CGlobalFunctionData::EmptyFunction()
{
	static const CRuntimeFunctionPtr emptyFunction( new CEmptyFunction );
	return emptyFunction;
}

bool CGlobalFunctionData::IsEmbeddedFunction() const
{
	return ( embeddedFunction != nullptr );
}

bool CGlobalFunctionData::IsPreparatoryFunction() const
{
	return ( preparatoryFunction != nullptr );
}

bool CGlobalFunctionData::IsDefined() const
{
	return ( IsEmbeddedFunction() || IsPreparatoryFunction() );
}

void CGlobalFunctionData::SetPreparatoryFunction(
	const CPreparatoryFunction* const _preparatoryFunction,
	const TRuntimeModuleId _runtimeModuleId )
{
	assert( !IsDefined() );
	assert( _preparatoryFunction != nullptr );
	preparatoryFunction = _preparatoryFunction;
	runtimeModuleId = _runtimeModuleId;
}

const CPreparatoryFunction* CGlobalFunctionData::PreparatoryFunction() const
{
	assert( IsPreparatoryFunction() );
	return preparatoryFunction;
}

const TRuntimeModuleId CGlobalFunctionData::RuntimeModuleId() const
{
	assert( IsPreparatoryFunction() );
	return runtimeModuleId;
}

void CGlobalFunctionData::SetEmbeddedFunction(
	const TEmbeddedFunctionPtr _embeddedFunction )
{
	assert( !IsDefined() );
	assert( _embeddedFunction != nullptr );
	embeddedFunction = _embeddedFunction;
}

TEmbeddedFunctionPtr CGlobalFunctionData::EmbeddedFunction() const
{
	assert( IsEmbeddedFunction() );
	return embeddedFunction;
}

CRuntimeFunctionPtr CGlobalFunctionData::RuntimeFunction() const
{
	if( !static_cast<bool>( runtimeFunction ) ) {
		if( IsEmbeddedFunction() ) {
			runtimeFunction.reset( new CEmbeddedFunction(
				embeddedFunction ) );
		} else {
			assert( IsPreparatoryFunction() );
			if( PreparatoryFunction()->IsEmpty() ) {
				runtimeFunction = EmptyFunction();
			} else {
				runtimeFunction.reset( new COrdinaryFunction(
					PreparatoryFunction()->FirstOperation(),
					RuntimeModuleId() ) );
			}
		}
	}
	return runtimeFunction;
}

//-----------------------------------------------------------------------------
// CInternalProgramBuilder

const char* const ProgramStartFunctionName = "go";

class CInternalProgramBuilder {
public:
	static CProgramPtr Build( CModuleDataVector& modules,
		CErrorsHelper& errors );

private:
	CErrorsHelper& errors;
	CDictionary<CGlobalFunctionData, std::string> globals;
	CProgramPtr program;

	CInternalProgramBuilder( CErrorsHelper& _errors, int numberOfModules );

	typedef void ( CInternalProgramBuilder::*TProcessFunctionPtr )(
		CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );

	void processModules( const CModuleDataVector& modules,
		const TProcessFunctionPtr processFunction );

	void collectFunction( CPreparatoryFunction* function,
		 const TRuntimeModuleId runtimeModuleId );
	void collect( const CModuleDataVector& modules );

	void check();

	void compileFunction( CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );
	void compile( const CModuleDataVector& modules );

	void linkFunction( CPreparatoryFunction* function,
		const TRuntimeModuleId runtimeModuleId );
	void link( const CModuleDataVector& modules );
};

//-----------------------------------------------------------------------------

CInternalProgramBuilder::CInternalProgramBuilder( CErrorsHelper& _errors,
		int numberOfModules ) :
	errors( _errors ),
	program( new CProgram( numberOfModules ) )
{
	assert( static_cast<bool>( program ) );
	// add standart functions
	for( int i = 0; standartEmbeddedFunctions[i].EmbeddedFunction != nullptr;
		i++ )
	{
		assert( standartEmbeddedFunctions[i].externalName != nullptr );
		std::string externalName = standartEmbeddedFunctions[i].externalName;
		assert( !externalName.empty() );
		const int globalIndex = globals.AddKey( externalName );
		CGlobalFunctionData& global = globals.GetData( globalIndex );
		global.SetEmbeddedFunction(
			standartEmbeddedFunctions[i].EmbeddedFunction );
	}
	// add `ProgramStartFunctionName`
	const int globalGoIndex = globals.AddKey( ProgramStartFunctionName );
	assert( !globals.GetData( globalGoIndex ).IsDefined() );
}

CProgramPtr CInternalProgramBuilder::Build( CModuleDataVector& modules,
	CErrorsHelper& errors )
{
	assert( !errors.HasErrors() );
	assert( !modules.empty() );
	CInternalProgramBuilder builder( errors, modules.size() );
	builder.collect( modules );
	if( !errors.HasErrors() ) {
		builder.check();
		if( !errors.HasErrors() ) {
			builder.compile( modules );
			assert( !errors.HasErrors() );
			builder.link( modules );
			assert( !errors.HasErrors() );
			return builder.program;
		}
	}
	modules.clear();
	return nullptr;
}

void CInternalProgramBuilder::processModules( const CModuleDataVector& modules,
	const TProcessFunctionPtr processFunction )
{
	TRuntimeModuleId currentModuleId = 0;
	for( CModuleDataVector::const_iterator module = modules.begin();
		module != modules.end(); ++module )
	{
		const CPreparatoryFunctions& functions = ( *module )->Functions;
		for( int i = 0; i < functions.Size(); i++ ) {
			( this->*processFunction )( functions.GetData( i ).get(),
				currentModuleId );
		}
		currentModuleId++;
	}
}

void CInternalProgramBuilder::collectFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId runtimeModuleId )
{
	if( !function->IsExternal() && !function->IsEntry() ) {
		return;
	}

	const int globalIndex = globals.AddKey( function->ExternalName() );
	if( function->IsEntry() ) {
		assert( !function->IsExternal() );
		CGlobalFunctionData& global = globals.GetData( globalIndex );
		if( global.IsDefined() ) {
			std::ostringstream stringStream;
			stringStream << "function with external name `"
				<< function->ExternalNameToken().word
				<< "` already defined in program";
			errors.Error( stringStream.str() );
		} else {
			global.SetPreparatoryFunction( function, runtimeModuleId );
		}
	}
}

void CInternalProgramBuilder::collect( const CModuleDataVector& modules )
{
	processModules( modules, &CInternalProgramBuilder::collectFunction );
}

void CInternalProgramBuilder::check()
{
	for( int globalIndex = 0; globalIndex < globals.Size(); globalIndex++ )
	{
		if( !globals.GetData( globalIndex ).IsDefined() ) {
			std::ostringstream stringStream;
			stringStream << "function with external name `"
				<< globals.GetKey( globalIndex ) << "` was not defined";
			errors.Error( stringStream.str() );
		}
	}
}

void CInternalProgramBuilder::compileFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId /*runtimeModuleId*/ )
{
	CFunctionCompiler compiler( program->OperationsHolder() );
	if( function->IsOrdinary() ) {
		function->Compile( compiler );
	}
}

void CInternalProgramBuilder::compile( const CModuleDataVector& modules )
{
	processModules( modules, &CInternalProgramBuilder::compileFunction );
}

void CInternalProgramBuilder::linkFunction( CPreparatoryFunction* function,
	const TRuntimeModuleId runtimeModuleId )
{
	CRuntimeFunctions& functions = program->Module( runtimeModuleId ).Functions;
	const int functionKey = functions.AddKey( function->Name() );
	CRuntimeFunctionPtr& runtimeFunction = functions.GetData( functionKey );
	assert( !static_cast<bool>( runtimeFunction ) );

	switch( function->GetType() ) {
		case PFT_Empty:
			runtimeFunction = CGlobalFunctionData::EmptyFunction();
			break;
		case PFT_Compiled:
			runtimeFunction.reset( new COrdinaryFunction(
				function->FirstOperation(), runtimeModuleId ) );
			break;
		case PFT_External:
		{
			const int globalIndex = globals.FindKey( function->ExternalName() );
			runtimeFunction = globals.GetData( globalIndex ).RuntimeFunction();
			break;
		}
		default:
			assert( false );
			break;
	}
}

void CInternalProgramBuilder::link( const CModuleDataVector& modules )
{
	const CGlobalFunctionData& goFunction = globals.GetData( globals.FindKey(
		ProgramStartFunctionName ) );
	TOperationAddress goFirstOperation =
		goFunction.PreparatoryFunction()->FirstOperation();
	TRuntimeModuleId goRuntimeModuleId = goFunction.RuntimeModuleId();
	// link all
	processModules( modules, &CInternalProgramBuilder::linkFunction );
	// set program start function
	const CRuntimeFunctions& runtimeFunctions =
		program->Module( goRuntimeModuleId ).Functions;
	TLabel goLabel = InvalidDictionaryIndex;
	for( int i = 0; i < runtimeFunctions.Size(); i++ ) {
		if( runtimeFunctions.GetData( i )->IsOrdinary()
			&& static_cast<const COrdinaryFunction*>(
				runtimeFunctions.GetData( i ).get() )->FirstOperation() ==
				goFirstOperation )
		{
			goLabel = i;
		}
	}
	assert( goLabel != InvalidDictionaryIndex );
	program->SetProgramStartFunction( goLabel + goRuntimeModuleId * LabelMask );
}

//-----------------------------------------------------------------------------
// CProgramBuilder

CProgramBuilder::CProgramBuilder( IErrorHandler* errorHandler ) :
	CFunctionBuilder( errorHandler )
{
	Reset();
}

void CProgramBuilder::Reset()
{
	CFunctionBuilder::Reset();
}

void CProgramBuilder::AddModule( CModuleDataPtr& module )
{
	modules.push_back( CModuleDataPtr( module.release() ) );
}

CProgramPtr CProgramBuilder::BuildProgram()
{
	return CInternalProgramBuilder::Build( modules, *this );
}

//-----------------------------------------------------------------------------

} // end of namespace Refal2
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE JPetSinogramTest
#include <boost/test/unit_test.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <vector>
#include <utility>
#include <fstream>
#include <iostream>
using namespace boost::numeric::ublas;

#include "./JPetSinogram.h"

BOOST_AUTO_TEST_SUITE(FirstSuite)

BOOST_AUTO_TEST_CASE( sinogram )
{

  std::ifstream in;
  in.open("phantom.pgm");
  std::string line;
  getline(in, line);
  if(line != "P2") { /*error*/ }

  unsigned int width;
  unsigned int height;
  in >> width;
  in >> height;
  std::cout << line << std::endl;
  std::cout << width << " " << height << std::endl;
  int val;
  in >> val; // skip max val
  matrix<int> m (width, height);
  for(unsigned int i = 0; i < width; i++) {
    for(unsigned int j = 0; j < height; j++) {
      in >> val;
      m(i, j) = val;
    }
  }
  //m(30, 30) = 100;
  JPetSinogram sin;
  std::vector<std::vector<double>> result = sin.sinogram(m, 180, 256); // value, theta, s
  std::ofstream res;
  res.open("image.ppm");
  res << "P2" << std::endl;
  res << width << " " << height << std::endl;
  res << "255" << std::endl;

  for (int i = 0; i < 180; i++ ) {
    for (int j = 0; j < 256; j++ ) {
      res << (int)result[i][j] << " "; //barray[i + j * views] = (byte) projection[i][j];
    }
    res << std::endl;
  }

  for(std::vector<double> p : result) {
    for(double r : p)
      res << (int)r << " ";
    res << std::endl;
  }
  res.close();
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>correct image size<commit_after>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE JPetSinogramTest
#include <boost/test/unit_test.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <vector>
#include <utility>
#include <fstream>
#include <iostream>
using namespace boost::numeric::ublas;

#include "./JPetSinogram.h"

BOOST_AUTO_TEST_SUITE(FirstSuite)

BOOST_AUTO_TEST_CASE( sinogram )
{

  std::ifstream in;
  in.open("phantom.pgm");
  std::string line;
  getline(in, line);
  if(line != "P2") { /*error*/ }

  unsigned int width;
  unsigned int height;
  in >> width;
  in >> height;
  std::cout << line << std::endl;
  std::cout << width << " " << height << std::endl;
  int val;
  in >> val; // skip max val
  matrix<int> m (width, height);
  for(unsigned int i = 0; i < width; i++) {
    for(unsigned int j = 0; j < height; j++) {
      in >> val;
      m(i, j) = val;
    }
  }
  //m(30, 30) = 100;
  JPetSinogram sin;
  int views = 180;
  int scans = 256;
  std::vector<std::vector<double>> result = sin.sinogram(m, views, scans); // value, theta, s
  std::ofstream res;
  res.open("image.ppm");
  res << "P2" << std::endl;
  res << scans << " " << views << std::endl;
  res << "255" << std::endl;

  for (int i = 0; i < views; i++ ) {
    for (int j = 0; j < scans; j++ ) {
      res << (int)result[i][j] << " "; //barray[i + j * views] = (byte) projection[i][j];
    }
    res << std::endl;
  }

  for(std::vector<double> p : result) {
    for(double r : p)
      res << (int)r << " ";
    res << std::endl;
  }
  res.close();
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>