<commit_before>#include "mini_stdint.h"

#define WEAK __attribute__((weak))

extern "C" {

#ifdef BITS_64
#define WIN32API
#else
#define WIN32API __stdcall
#endif

extern char *getenv(const char *);
extern int atoi(const char *);

extern int halide_printf(void *user_context, const char *, ...);

#ifndef NULL
#define NULL 0
#endif

// These sizes are large enough for 32-bit and 64-bit
typedef uint64_t ConditionVariable;
typedef uint64_t InitOnce;
typedef uint64_t Thread;
typedef struct {
    uint8_t buf[40];
} CriticalSection;

extern WIN32API Thread CreateThread(void *, size_t, void *(*fn)(void *), void *, int32_t, int32_t *);
extern WIN32API void InitializeConditionVariable(ConditionVariable *);
extern WIN32API void WakeAllConditionVariable(ConditionVariable *);
extern WIN32API void SleepConditionVariableCS(ConditionVariable *, CriticalSection *, int);
extern WIN32API void InitializeCriticalSection(CriticalSection *);
extern WIN32API void DeleteCriticalSection(CriticalSection *);
extern WIN32API void EnterCriticalSection(CriticalSection *);
extern WIN32API void LeaveCriticalSection(CriticalSection *);
extern WIN32API int32_t WaitForSingleObject(Thread, int32_t timeout);
extern WIN32API bool InitOnceExecuteOnce(InitOnce *,
  bool WIN32API (*f)(InitOnce *, void *, void **), void *, void **);

struct work {
    work *next_job;
    int (*f)(void *, int, uint8_t *);
    void *user_context;
    int next, max;
    uint8_t *closure;
    int active_workers;
    int exit_status;
    bool running() { return next < max || active_workers > 0; }
};

// The work queue and thread pool is weak, so one big work queue is shared by all halide functions
#define MAX_THREADS 64
WEAK struct {
    // Initialization of the critical section is guarded by this
    InitOnce init_once;

    // all fields are protected by this mutex.
    CriticalSection mutex;

    // Singly linked list for job stack
    work *jobs;

    // Broadcast whenever items are added to the queue or a job completes.
    ConditionVariable state_change;
    // Keep track of threads so they can be joined at shutdown
    Thread threads[MAX_THREADS];

    // Global flag indicating
    bool shutdown;

    bool running() {
        return !shutdown;
    }

} halide_work_queue;

WEAK bool WIN32API InitOnceCallback(InitOnce *, void *, void **) {
    InitializeCriticalSection(&halide_work_queue.mutex);
    return true;
}

WEAK int halide_threads;
WEAK bool halide_thread_pool_initialized = false;

WEAK void halide_shutdown_thread_pool() {
    if (!halide_thread_pool_initialized) return;

    // Wake everyone up and tell them the party's over and it's time
    // to go home
    EnterCriticalSection(&halide_work_queue.mutex);
    halide_work_queue.shutdown = true;
    WakeAllConditionVariable(&halide_work_queue.state_change);
    LeaveCriticalSection(&halide_work_queue.mutex);

    // Wait until they leave
    for (int i = 0; i < halide_threads-1; i++) {
        //fprintf(stderr, "Waiting for thread %d to exit\n", i);
        WaitForSingleObject(halide_work_queue.threads[i], -1);
    }

    //fprintf(stderr, "All threads have quit. Destroying mutex and condition variable.\n");
    // Tidy up
    DeleteCriticalSection(&halide_work_queue.mutex);
    halide_work_queue.init_once = 0;
    //DestroyConditionVariable(&halide_work_queue.state_change);
    halide_thread_pool_initialized = false;
}

typedef int (*halide_task)(void *user_context, int, uint8_t *);

WEAK int (*halide_custom_do_task)(void *user_context, halide_task, int, uint8_t *);

WEAK void halide_set_custom_do_task(int (*f)(void *, halide_task, int, uint8_t *)) {
    halide_custom_do_task = f;
}

WEAK int (*halide_custom_do_par_for)(void *, halide_task, int, int, uint8_t *);

WEAK void halide_set_custom_do_par_for(int (*f)(void *, halide_task, int, int, uint8_t *)) {
    halide_custom_do_par_for = f;
}

WEAK int halide_do_task(void *user_context, halide_task f, int idx,
                        uint8_t *closure) {
    if (halide_custom_do_task) {
        return (*halide_custom_do_task)(user_context, f, idx, closure);
    } else {
        return f(user_context, idx, closure);
    }
}

WEAK void *halide_worker_thread(void *void_arg) {
    work *owned_job = (work *)void_arg;

    // halide_printf(NULL, "Worker starting\n");

    // Grab the lock
    EnterCriticalSection(&halide_work_queue.mutex);

    // If I'm a job owner, then I was the thread that called
    // do_par_for, and I should only stay in this function until my
    // job is complete. If I'm a lowly worker thread, I should stay in
    // this function as long as the work queue is running.
    while (owned_job != NULL ? owned_job->running()
           : halide_work_queue.running()) {

        if (halide_work_queue.jobs == NULL) {
            // There are no jobs pending, though some tasks may still
            // be in flight from the last job. Release the lock and
            // wait for something new to happen.
            // halide_printf(NULL, "Worker sleeping\n");
            SleepConditionVariableCS(&halide_work_queue.state_change, &halide_work_queue.mutex, -1);
        } else {
            // There are jobs still to do. Grab the next one.
            work *job = halide_work_queue.jobs;

            // Claim a task from it.
            work myjob = *job;
            job->next++;

            // If there were no more tasks pending for this job, or if
            // it has failed, remove it from the stack.
            if (job->next == job->max) {
                halide_work_queue.jobs = job->next_job;
            }

            // Increment the active_worker count so that other threads
            // are aware that this job is still in progress even
            // though there are no outstanding tasks for it.
            job->active_workers++;

            // Release the lock and do the task.
            // halide_printf(NULL, "Worker about to work\n");
            LeaveCriticalSection(&halide_work_queue.mutex);
            // halide_printf(NULL, "Worker doing work\n");
            int result = halide_do_task(myjob.user_context, myjob.f, myjob.next,
                                        myjob.closure);
            EnterCriticalSection(&halide_work_queue.mutex);
            // halide_printf(NULL, "Worker done work\n");

            // If this task failed, set the exit status on the job.
            if (result) {
                job->exit_status = result;
            }

            // We are no longer active on this job
            job->active_workers--;

            // If the job is done and I'm not the owner of it, wake up
            // the owner.
            if (!job->running() && job != owned_job) {
                // halide_printf(NULL, "Job done. Wake up the owner.\n");
                WakeAllConditionVariable(&halide_work_queue.state_change);
            }
        }
    }
    // halide_printf(NULL, "Worker quitting\n");
    LeaveCriticalSection(&halide_work_queue.mutex);
    return NULL;
}

WEAK int halide_do_par_for(void *user_context, int (*f)(void *, int, uint8_t *),
                           int min, int size, uint8_t *closure) {
    if (halide_custom_do_par_for) {
        return (*halide_custom_do_par_for)(user_context, f, min, size, closure);
    }

    // halide_printf(user_context, "In do_par_for\n");

    // Create the mutex
    InitOnceExecuteOnce(&halide_work_queue.init_once, InitOnceCallback, NULL, NULL);

    // halide_printf(user_context, "Grabbing mutex\n");

    // Grab it
    EnterCriticalSection(&halide_work_queue.mutex);

    if (!halide_thread_pool_initialized) {
        halide_work_queue.shutdown = false;

        //  halide_printf(user_context, "Making condition variable\n");

        InitializeConditionVariable(&halide_work_queue.state_change);
        halide_work_queue.jobs = NULL;

        char *threadStr = getenv("HL_NUMTHREADS");
        if (!threadStr) {
            threadStr = getenv("NUMBER_OF_PROCESSORS"); // Apparently a standard windows environment variable
        }
        if (threadStr) {
            halide_threads = atoi(threadStr);
        } else {
            halide_threads = 8;
            // halide_printf(user_context, "HL_NUMTHREADS not defined. Defaulting to %d threads.\n", halide_threads);
        }
        if (halide_threads > MAX_THREADS) {
            halide_threads = MAX_THREADS;
        } else if (halide_threads < 1) {
            halide_threads = 1;
        }
        for (int i = 0; i < halide_threads-1; i++) {
            // halide_printf(user_context, "Creating thread %d\n", i);
            halide_work_queue.threads[i] = CreateThread(NULL, 0, halide_worker_thread, NULL, 0, NULL);
        }

        halide_thread_pool_initialized = true;
    }

    // Make the job.
    work job;
    job.f = f;               // The job should call this function. It takes an index and a closure.
    job.user_context = user_context;
    job.next = min;          // Start at this index.
    job.max  = min + size;   // Keep going until one less than this index.
    job.closure = closure;   // Use this closure.
    job.exit_status = 0;     // The job hasn't failed yet
    job.active_workers = 0;  // Nobody is working on this yet

    // Push the job onto the stack.
    job.next_job = halide_work_queue.jobs;
    halide_work_queue.jobs = &job;

    // halide_printf(user_context, "Releasing mutex\n");
    LeaveCriticalSection(&halide_work_queue.mutex);

    // Wake up any idle worker threads.
    // halide_printf(user_context, "Waking up workers\n");
    WakeAllConditionVariable(&halide_work_queue.state_change);

    // Do some work myself.
    halide_worker_thread((void *)(&job));

    // Return zero if the job succeeded, otherwise return the exit
    // status of one of the failing jobs (whichever one failed last).
    return job.exit_status;
}

}
<commit_msg>Fix type of Thread in windows thread pool<commit_after>#include "mini_stdint.h"

#define WEAK __attribute__((weak))

extern "C" {

#ifdef BITS_64
#define WIN32API
#else
#define WIN32API __stdcall
#endif

extern char *getenv(const char *);
extern int atoi(const char *);

extern int halide_printf(void *user_context, const char *, ...);

#ifndef NULL
#define NULL 0
#endif

// These sizes are large enough for 32-bit and 64-bit
typedef uint64_t ConditionVariable;
typedef uint64_t InitOnce;
typedef void * Thread;
typedef struct {
    uint8_t buf[40];
} CriticalSection;

extern WIN32API Thread CreateThread(void *, size_t, void *(*fn)(void *), void *, int32_t, int32_t *);
extern WIN32API void InitializeConditionVariable(ConditionVariable *);
extern WIN32API void WakeAllConditionVariable(ConditionVariable *);
extern WIN32API void SleepConditionVariableCS(ConditionVariable *, CriticalSection *, int);
extern WIN32API void InitializeCriticalSection(CriticalSection *);
extern WIN32API void DeleteCriticalSection(CriticalSection *);
extern WIN32API void EnterCriticalSection(CriticalSection *);
extern WIN32API void LeaveCriticalSection(CriticalSection *);
extern WIN32API int32_t WaitForSingleObject(Thread, int32_t timeout);
extern WIN32API bool InitOnceExecuteOnce(InitOnce *, bool WIN32API (*f)(InitOnce *, void *, void **), void *, void **);

struct work {
    work *next_job;
    int (*f)(void *, int, uint8_t *);
    void *user_context;
    int next, max;
    uint8_t *closure;
    int active_workers;
    int exit_status;
    bool running() { return next < max || active_workers > 0; }
};

// The work queue and thread pool is weak, so one big work queue is shared by all halide functions
#define MAX_THREADS 64
WEAK struct {
    // Initialization of the critical section is guarded by this
    InitOnce init_once;

    // all fields are protected by this mutex.
    CriticalSection mutex;

    // Singly linked list for job stack
    work *jobs;

    // Broadcast whenever items are added to the queue or a job completes.
    ConditionVariable state_change;
    // Keep track of threads so they can be joined at shutdown
    Thread threads[MAX_THREADS];

    // Global flag indicating
    bool shutdown;

    bool running() {
        return !shutdown;
    }

} halide_work_queue;

WEAK bool WIN32API InitOnceCallback(InitOnce *, void *, void **) {
    InitializeCriticalSection(&halide_work_queue.mutex);
    return true;
}

WEAK int halide_threads;
WEAK bool halide_thread_pool_initialized = false;

WEAK void halide_shutdown_thread_pool() {
    if (!halide_thread_pool_initialized) return;

    // Wake everyone up and tell them the party's over and it's time
    // to go home
    EnterCriticalSection(&halide_work_queue.mutex);
    halide_work_queue.shutdown = true;
    WakeAllConditionVariable(&halide_work_queue.state_change);
    LeaveCriticalSection(&halide_work_queue.mutex);

    // Wait until they leave
    for (int i = 0; i < halide_threads-1; i++) {
        //fprintf(stderr, "Waiting for thread %d to exit\n", i);
        WaitForSingleObject(halide_work_queue.threads[i], -1);
    }

    //fprintf(stderr, "All threads have quit. Destroying mutex and condition variable.\n");
    // Tidy up
    DeleteCriticalSection(&halide_work_queue.mutex);
    halide_work_queue.init_once = 0;
    //DestroyConditionVariable(&halide_work_queue.state_change);
    halide_thread_pool_initialized = false;
}

typedef int (*halide_task)(void *user_context, int, uint8_t *);

WEAK int (*halide_custom_do_task)(void *user_context, halide_task, int, uint8_t *);

WEAK void halide_set_custom_do_task(int (*f)(void *, halide_task, int, uint8_t *)) {
    halide_custom_do_task = f;
}

WEAK int (*halide_custom_do_par_for)(void *, halide_task, int, int, uint8_t *);

WEAK void halide_set_custom_do_par_for(int (*f)(void *, halide_task, int, int, uint8_t *)) {
    halide_custom_do_par_for = f;
}

WEAK int halide_do_task(void *user_context, halide_task f, int idx,
                        uint8_t *closure) {
    if (halide_custom_do_task) {
        return (*halide_custom_do_task)(user_context, f, idx, closure);
    } else {
        return f(user_context, idx, closure);
    }
}

WEAK void *halide_worker_thread(void *void_arg) {
    work *owned_job = (work *)void_arg;

    // halide_printf(NULL, "Worker starting\n");

    // Grab the lock
    EnterCriticalSection(&halide_work_queue.mutex);

    // If I'm a job owner, then I was the thread that called
    // do_par_for, and I should only stay in this function until my
    // job is complete. If I'm a lowly worker thread, I should stay in
    // this function as long as the work queue is running.
    while (owned_job != NULL ? owned_job->running()
           : halide_work_queue.running()) {

        if (halide_work_queue.jobs == NULL) {
            // There are no jobs pending, though some tasks may still
            // be in flight from the last job. Release the lock and
            // wait for something new to happen.
            // halide_printf(NULL, "Worker sleeping\n");
            SleepConditionVariableCS(&halide_work_queue.state_change, &halide_work_queue.mutex, -1);
        } else {
            // There are jobs still to do. Grab the next one.
            work *job = halide_work_queue.jobs;

            // Claim a task from it.
            work myjob = *job;
            job->next++;

            // If there were no more tasks pending for this job, or if
            // it has failed, remove it from the stack.
            if (job->next == job->max) {
                halide_work_queue.jobs = job->next_job;
            }

            // Increment the active_worker count so that other threads
            // are aware that this job is still in progress even
            // though there are no outstanding tasks for it.
            job->active_workers++;

            // Release the lock and do the task.
            // halide_printf(NULL, "Worker about to work\n");
            LeaveCriticalSection(&halide_work_queue.mutex);
            // halide_printf(NULL, "Worker doing work\n");
            int result = halide_do_task(myjob.user_context, myjob.f, myjob.next,
                                        myjob.closure);
            EnterCriticalSection(&halide_work_queue.mutex);
            // halide_printf(NULL, "Worker done work\n");

            // If this task failed, set the exit status on the job.
            if (result) {
                job->exit_status = result;
            }

            // We are no longer active on this job
            job->active_workers--;

            // If the job is done and I'm not the owner of it, wake up
            // the owner.
            if (!job->running() && job != owned_job) {
                // halide_printf(NULL, "Job done. Wake up the owner.\n");
                WakeAllConditionVariable(&halide_work_queue.state_change);
            }
        }
    }
    // halide_printf(NULL, "Worker quitting\n");
    LeaveCriticalSection(&halide_work_queue.mutex);
    return NULL;
}

WEAK int halide_do_par_for(void *user_context, int (*f)(void *, int, uint8_t *),
                           int min, int size, uint8_t *closure) {
    if (halide_custom_do_par_for) {
        return (*halide_custom_do_par_for)(user_context, f, min, size, closure);
    }

    // halide_printf(user_context, "In do_par_for\n");

    // Create the mutex
    InitOnceExecuteOnce(&halide_work_queue.init_once, InitOnceCallback, NULL, NULL);

    // halide_printf(user_context, "Grabbing mutex\n");

    // Grab it
    EnterCriticalSection(&halide_work_queue.mutex);

    if (!halide_thread_pool_initialized) {
        halide_work_queue.shutdown = false;

        //  halide_printf(user_context, "Making condition variable\n");

        InitializeConditionVariable(&halide_work_queue.state_change);
        halide_work_queue.jobs = NULL;

        char *threadStr = getenv("HL_NUMTHREADS");
        if (!threadStr) {
            threadStr = getenv("NUMBER_OF_PROCESSORS"); // Apparently a standard windows environment variable
        }
        if (threadStr) {
            halide_threads = atoi(threadStr);
        } else {
            halide_threads = 8;
            // halide_printf(user_context, "HL_NUMTHREADS not defined. Defaulting to %d threads.\n", halide_threads);
        }
        if (halide_threads > MAX_THREADS) {
            halide_threads = MAX_THREADS;
        } else if (halide_threads < 1) {
            halide_threads = 1;
        }
        for (int i = 0; i < halide_threads-1; i++) {
            // halide_printf(user_context, "Creating thread %d\n", i);
            halide_work_queue.threads[i] = CreateThread(NULL, 0, halide_worker_thread, NULL, 0, NULL);
        }

        halide_thread_pool_initialized = true;
    }

    // Make the job.
    work job;
    job.f = f;               // The job should call this function. It takes an index and a closure.
    job.user_context = user_context;
    job.next = min;          // Start at this index.
    job.max  = min + size;   // Keep going until one less than this index.
    job.closure = closure;   // Use this closure.
    job.exit_status = 0;     // The job hasn't failed yet
    job.active_workers = 0;  // Nobody is working on this yet

    // Push the job onto the stack.
    job.next_job = halide_work_queue.jobs;
    halide_work_queue.jobs = &job;

    // halide_printf(user_context, "Releasing mutex\n");
    LeaveCriticalSection(&halide_work_queue.mutex);

    // Wake up any idle worker threads.
    // halide_printf(user_context, "Waking up workers\n");
    WakeAllConditionVariable(&halide_work_queue.state_change);

    // Do some work myself.
    halide_worker_thread((void *)(&job));

    // Return zero if the job succeeded, otherwise return the exit
    // status of one of the failing jobs (whichever one failed last).
    return job.exit_status;
}

}
<|endoftext|>


<commit_before>/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "spriterenderercomponent.h"

#include <graphics/item.h>
#include <gameobject.h>
#include <core/debughelper.h>

#include <iostream>
#include <asset.h>
#include <QMimeData>

#include <graphics/meshes/polygonmesh.h>
#include <graphics/meshes/spritemesh.h>

REGISTER_OBJECTTYPE(GluonEngine, SpriteRendererComponent)

Q_DECLARE_METATYPE(GluonCore::GluonObject*);
Q_DECLARE_METATYPE(GluonEngine::Asset*);

using namespace GluonEngine;

class SpriteRendererComponent::SpriteRendererComponentPrivate
{
    public:
        SpriteRendererComponentPrivate()
        {
            item = 0;
            mesh = 0;
            size.setHeight(1.0f);
            size.setWidth(1.0f);
            color.setRgb(255, 255, 255);
        }

        GluonGraphics::Item *item;
        GluonGraphics::SpriteMesh *mesh;

        QColor color;
        QSizeF size;
};

SpriteRendererComponent::SpriteRendererComponent ( QObject* parent ) : Component ( parent )
{
    d = new SpriteRendererComponentPrivate;

    QVariant somethingEmpty;
    Asset *theObject = 0;
    somethingEmpty.setValue<GluonEngine::Asset*>(theObject);
    setProperty("texture", somethingEmpty);
}

SpriteRendererComponent::SpriteRendererComponent ( const SpriteRendererComponent& other )
    : Component ( other ),
    d(other.d)
{
}

SpriteRendererComponent::~SpriteRendererComponent()
{
    stop();

    delete d;
}

void SpriteRendererComponent::start()
{
    if(!d->item) {
        d->mesh = new GluonGraphics::SpriteMesh(d->size);
        d->item = new GluonGraphics::Item(d->mesh);
        d->item->setColor(d->color);
    }

    if(texture()) {
        if(!texture()->isLoaded())
            texture()->load();

        const QMimeData* data = texture()->data();
        if(data->hasImage()) {
            d->item->mesh()->setTexture(data->imageData().value<QImage>());
        }
    }
}

void SpriteRendererComponent::draw ( int timeLapse )
{
    Q_UNUSED(timeLapse)

    if(d->item) {
        d->item->setMatrix(gameObject()->transform());
    }
}

void SpriteRendererComponent::update ( int elapsedMilliseconds )
{
    Q_UNUSED(elapsedMilliseconds)
}

void SpriteRendererComponent::stop()
{
    if(d->item) {
        delete d->item;
        delete d->mesh;
        d->item = 0;
        d->mesh = 0;
    }
}

void SpriteRendererComponent::setSize ( const QSizeF &size )
{
    d->size = size;

    if(d->mesh) {
        d->mesh->setSize(size);
    }
}

QSizeF SpriteRendererComponent::size()
{
    return d->size;
}

void SpriteRendererComponent::setColor(const QColor& color)
{
    d->color = color;
    if(d->mesh) {
        d->mesh->setColor(color);
    }
}

void SpriteRendererComponent::setColor(int r, int g, int b, int a)
{
    setColor(QColor(r, g, b, a));
}

QColor SpriteRendererComponent::color()
{
    return d->color;
}

Asset* SpriteRendererComponent::texture()
{
    GluonObject* obj = property("texture").value<GluonCore::GluonObject*>();
    return qobject_cast< GluonEngine::Asset* >(obj);
}

void SpriteRendererComponent::setTexture(Asset* asset)
{
    QVariant somethingEmpty;
    somethingEmpty.setValue<GluonEngine::Asset*>(asset);
    setProperty("texture", somethingEmpty);
}

Q_EXPORT_PLUGIN2(gluon_component_spriterenderer, GluonEngine::SpriteRendererComponent);

#include "spriterenderercomponent.moc"
<commit_msg>Minor fixes to spriterenderer<commit_after>/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "spriterenderercomponent.h"

#include <graphics/item.h>
#include <gameobject.h>
#include <core/debughelper.h>

#include <iostream>
#include <asset.h>
#include <QMimeData>

#include <graphics/meshes/polygonmesh.h>
#include <graphics/meshes/spritemesh.h>

REGISTER_OBJECTTYPE(GluonEngine, SpriteRendererComponent)

Q_DECLARE_METATYPE(GluonCore::GluonObject*);
Q_DECLARE_METATYPE(GluonEngine::Asset*);

using namespace GluonEngine;

class SpriteRendererComponent::SpriteRendererComponentPrivate
{
    public:
        SpriteRendererComponentPrivate()
        {
            item = 0;
            mesh = 0;
            size = QSizeF(1.0f, 1.0f);
            color.setRgb(255, 255, 255);
        }

        GluonGraphics::Item *item;
        GluonGraphics::SpriteMesh *mesh;

        QColor color;
        QSizeF size;
};

SpriteRendererComponent::SpriteRendererComponent ( QObject* parent ) : Component ( parent )
{
    d = new SpriteRendererComponentPrivate;

    QVariant somethingEmpty;
    Asset *theObject = 0;
    somethingEmpty.setValue<GluonEngine::Asset*>(theObject);
    setProperty("texture", somethingEmpty);
}

SpriteRendererComponent::SpriteRendererComponent ( const SpriteRendererComponent& other )
    : Component ( other ),
    d(other.d)
{
}

SpriteRendererComponent::~SpriteRendererComponent()
{
    stop();

    delete d;
}

void SpriteRendererComponent::start()
{
    if(!d->item) {
        d->mesh = new GluonGraphics::SpriteMesh(d->size, this);
        d->item = new GluonGraphics::Item(d->mesh, this);
        d->item->setColor(d->color);
    }

    if(texture()) {
        if(!texture()->isLoaded())
            texture()->load();

        const QMimeData* data = texture()->data();
        if(data->hasImage()) {
            d->mesh->setTexture(data->imageData().value<QImage>());
        }
    }
}

void SpriteRendererComponent::draw ( int timeLapse )
{
    Q_UNUSED(timeLapse)

    if(d->item) {
        d->item->setMatrix(gameObject()->transform());
    }
}

void SpriteRendererComponent::update ( int elapsedMilliseconds )
{
    Q_UNUSED(elapsedMilliseconds)
}

void SpriteRendererComponent::stop()
{
    if(d->item) {
        delete d->item;
        d->item = 0;
        d->mesh = 0;
    }
}

void SpriteRendererComponent::setSize ( const QSizeF &size )
{
    d->size = size;

    if(d->mesh) {
        d->mesh->setSize(size);
    }
}

QSizeF SpriteRendererComponent::size()
{
    return d->size;
}

void SpriteRendererComponent::setColor(const QColor& color)
{
    d->color = color;
    if(d->mesh) {
        d->mesh->setColor(color);
    }
}

void SpriteRendererComponent::setColor(int r, int g, int b, int a)
{
    setColor(QColor(r, g, b, a));
}

QColor SpriteRendererComponent::color()
{
    return d->color;
}

Asset* SpriteRendererComponent::texture()
{
    GluonObject* obj = property("texture").value<GluonCore::GluonObject*>();
    return qobject_cast< GluonEngine::Asset* >(obj);
}

void SpriteRendererComponent::setTexture(Asset* asset)
{
    QVariant somethingEmpty;
    somethingEmpty.setValue<GluonEngine::Asset*>(asset);
    setProperty("texture", somethingEmpty);
}

Q_EXPORT_PLUGIN2(gluon_component_spriterenderer, GluonEngine::SpriteRendererComponent);

#include "spriterenderercomponent.moc"
<|endoftext|>


<commit_before><commit_msg>Add: figTemplatePair figTemplateDark<commit_after><|endoftext|>


<commit_before>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include <thread>
#include <asyncply/run.h>

// TODO: remove
#include <set>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <exception>
#include <vector>
#include <algorithm>
#include <locale>
#include <boost/tokenizer.hpp>
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 7);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));

	// https://play.golang.org/
	/*
package main

import "fmt"

func worker(done chan int) {
    for i := 0; i < 50; i++ {
        fmt.Println("----> send ", i, " [PRE]")
	done <- i
        fmt.Println("----> send ", i, " [POST]")
    }
}

func main() {
    done := make(chan int, 8)
    go worker(done)
    for i := 0; i < 50; i++ {
	j := <- done
	fmt.Println("recv ", j, "  <---- ")
    }
}
	*/

	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

cu::channel<std::string>::link quot(const char* delim = "\"")
{
	return [&func](cu::channel<std::string>::in& source, cu::channel<std::string>::out& yield)
	{
		for (auto& s : source)
		{
			if(s)
			{
				std::stringstream ss;
				ss << delim << *s << delim;
				yield(ss.str());
			}
			else
			{
				// propagate error
				yield(s);
			}
		}
	};
}

TEST(ChannelTest, goroutines_consumer_unbuffered)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 0, quot("1__^-^__1"), quot("2__\o/__2"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(ChannelTest, goroutines_consumer_buffered_one)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 1);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(ChannelTest, goroutines_consumer_buffered_two)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 2);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch);
	// person2
	sch.spawn([&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn([&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn([&](auto& yield) {
		//
		other.wait(yield);
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<commit_msg>Update test_channel.cpp<commit_after>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include <thread>
#include <asyncply/run.h>

// TODO: remove
#include <set>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <exception>
#include <vector>
#include <algorithm>
#include <locale>
#include <boost/tokenizer.hpp>
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 7);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));

	// https://play.golang.org/
	/*
package main

import "fmt"

func worker(done chan int) {
    for i := 0; i < 50; i++ {
        fmt.Println("----> send ", i, " [PRE]")
	done <- i
        fmt.Println("----> send ", i, " [POST]")
    }
}

func main() {
    done := make(chan int, 8)
    go worker(done)
    for i := 0; i < 50; i++ {
	j := <- done
	fmt.Println("recv ", j, "  <---- ")
    }
}
	*/

	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

cu::channel<std::string>::link quot(const char* delim = "\"")
{
	return [=](cu::channel<std::string>::in& source, cu::channel<std::string>::out& yield)
	{
		for (auto& s : source)
		{
			if(s)
			{
				std::stringstream ss;
				ss << delim << *s << delim;
				yield(ss.str());
			}
			else
			{
				// propagate error
				yield(s);
			}
		}
	};
}

TEST(ChannelTest, goroutines_consumer_unbuffered)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 0, quot("1__^-^__1"), quot("2__\o/__2"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(ChannelTest, goroutines_consumer_buffered_one)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 1);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(ChannelTest, goroutines_consumer_buffered_two)
{
	cu::scheduler sch;
	cu::channel<std::string> go(sch, 2);
	// go.connect(cu::quote("__^-^__"));
	// go.connect(cu::quote("__\o/__"));
	sch.spawn([&](auto& yield) {
		for(;;)
		{
			auto data = go.get(yield);
 			if(data)
 			{
 				std::cout << "recv " << *data << " <----" << std::endl;
 			}
 			else
	 		{
			 	std::cout << "channel closed" << std::endl;
 				break;
 			}
		}
	});
	sch.spawn([&](auto& yield) {
		for(int i=0; i<50; ++i)
		{
			std::cout << "----> send " << i << " [PRE]" << std::endl;
			go(yield, std::to_string(i));
			std::cout << "----> send " << i << " [POST]" << std::endl;
		}
		go.close(yield);
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch);
	// person2
	sch.spawn([&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn([&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn([&](auto& yield) {
		//
		other.wait(yield);
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<|endoftext|>


<commit_before>#pragma once

#include "boost_defs.hpp"

BEGIN_BOOST_INCLUDE
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
END_BOOST_INCLUDE

#include "logger.hpp"
#include <atomic>
#include <chrono>
#include <thread>

namespace krbn {
class local_datagram_client final {
public:
  // Signals

  // Note: These signals are fired on non-main thread.

  boost::signals2::signal<void(void)> connected;
  boost::signals2::signal<void(const boost::system::error_code&)> connect_failed;
  boost::signals2::signal<void(void)> closed;

  // Methods

  local_datagram_client(const local_datagram_client&) = delete;

  local_datagram_client(void) : io_service_(),
                                work_(std::make_unique<boost::asio::io_service::work>(io_service_)),
                                socket_(io_service_),
                                connected_(false),
                                heartbeat_enabled_(false) {
    io_service_thread_ = std::thread([this] {
      (this->io_service_).run();
    });
  }

  ~local_datagram_client(void) {
    close();

    if (io_service_thread_.joinable()) {
      work_ = nullptr;
      io_service_thread_.join();
    }
  }

  void connect(const std::string& path) {
    close();

    io_service_.post([this, path] {
      connected_ = false;

      // open

      {
        boost::system::error_code error_code;
        socket_.open(boost::asio::local::datagram_protocol::socket::protocol_type(),
                     error_code);
        if (error_code) {
          connect_failed(error_code);
          return;
        }
      }

      // async_connect

      socket_.async_connect(boost::asio::local::datagram_protocol::endpoint(path),
                            [this](auto&& error_code) {
                              if (error_code) {
                                connect_failed(error_code);
                              } else {
                                connected_ = true;
                                connected();
                              }
                            });
    });
  }

  void close(void) {
    stop_heartbeat();

    io_service_.post([this] {
      boost::system::error_code error_code;

      socket_.cancel(error_code);
      socket_.close(error_code);

      if (connected_) {
        connected_ = false;
        closed();
      }
    });
  }

  void start_heartbeat(std::chrono::milliseconds heartbeat_interval) {
    stop_heartbeat();

    io_service_.post([this, heartbeat_interval] {
      heartbeat_enabled_ = true;
      heartbeat_thread_ = std::thread([this, heartbeat_interval] {
        std::vector<uint8_t> data;
        while (heartbeat_enabled_) {
          std::this_thread::sleep_for(heartbeat_interval);

          async_send(data);
        }
      });
    });
  }

  void stop_heartbeat(void) {
    io_service_.post([this] {
      heartbeat_enabled_ = false;

      if (heartbeat_thread_.joinable()) {
        heartbeat_thread_.join();
      }
    });
  }

  void async_send(const std::vector<uint8_t>& v) {
    auto ptr = std::make_shared<buffer>(v);
    io_service_.post([this, ptr] {
      do_async_send(ptr);
    });
  }

  void async_send(const uint8_t* _Nonnull p, size_t length) {
    auto ptr = std::make_shared<buffer>(p, length);
    io_service_.post([this, ptr] {
      do_async_send(ptr);
    });
  }

private:
  class buffer final {
  public:
    buffer(const std::vector<uint8_t> v) {
      v_ = v;
    }

    buffer(const uint8_t* _Nonnull p, size_t length) {
      v_.resize(length);
      memcpy(&(v_[0]), p, length);
    }

    const std::vector<uint8_t>& get_vector(void) const { return v_; }

  private:
    std::vector<uint8_t> v_;
  };

  void do_async_send(std::shared_ptr<buffer> ptr) {
    socket_.async_send(boost::asio::buffer(ptr->get_vector()),
                       [this, ptr](auto&& error_code,
                                   auto&& bytes_transferred) {
                         if (error_code) {
                           close();
                         }
                       });
  }

  boost::asio::io_service io_service_;
  std::unique_ptr<boost::asio::io_service::work> work_;
  boost::asio::local::datagram_protocol::socket socket_;
  std::thread io_service_thread_;
  bool connected_;

  std::thread heartbeat_thread_;
  std::atomic<bool> heartbeat_enabled_;
};
} // namespace krbn
<commit_msg>update comment<commit_after>#pragma once

#include "boost_defs.hpp"

BEGIN_BOOST_INCLUDE
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
END_BOOST_INCLUDE

#include "logger.hpp"
#include <atomic>
#include <chrono>
#include <thread>

namespace krbn {
class local_datagram_client final {
public:
  // Signals

  // Note: These signals are fired on local_datagram_client's thread.

  boost::signals2::signal<void(void)> connected;
  boost::signals2::signal<void(const boost::system::error_code&)> connect_failed;
  boost::signals2::signal<void(void)> closed;

  // Methods

  local_datagram_client(const local_datagram_client&) = delete;

  local_datagram_client(void) : io_service_(),
                                work_(std::make_unique<boost::asio::io_service::work>(io_service_)),
                                socket_(io_service_),
                                connected_(false),
                                heartbeat_enabled_(false) {
    io_service_thread_ = std::thread([this] {
      (this->io_service_).run();
    });
  }

  ~local_datagram_client(void) {
    close();

    if (io_service_thread_.joinable()) {
      work_ = nullptr;
      io_service_thread_.join();
    }
  }

  void connect(const std::string& path) {
    close();

    io_service_.post([this, path] {
      connected_ = false;

      // open

      {
        boost::system::error_code error_code;
        socket_.open(boost::asio::local::datagram_protocol::socket::protocol_type(),
                     error_code);
        if (error_code) {
          connect_failed(error_code);
          return;
        }
      }

      // async_connect

      socket_.async_connect(boost::asio::local::datagram_protocol::endpoint(path),
                            [this](auto&& error_code) {
                              if (error_code) {
                                connect_failed(error_code);
                              } else {
                                connected_ = true;
                                connected();
                              }
                            });
    });
  }

  void close(void) {
    stop_heartbeat();

    io_service_.post([this] {
      boost::system::error_code error_code;

      socket_.cancel(error_code);
      socket_.close(error_code);

      if (connected_) {
        connected_ = false;
        closed();
      }
    });
  }

  void start_heartbeat(std::chrono::milliseconds heartbeat_interval) {
    stop_heartbeat();

    io_service_.post([this, heartbeat_interval] {
      heartbeat_enabled_ = true;
      heartbeat_thread_ = std::thread([this, heartbeat_interval] {
        std::vector<uint8_t> data;
        while (heartbeat_enabled_) {
          std::this_thread::sleep_for(heartbeat_interval);

          async_send(data);
        }
      });
    });
  }

  void stop_heartbeat(void) {
    io_service_.post([this] {
      heartbeat_enabled_ = false;

      if (heartbeat_thread_.joinable()) {
        heartbeat_thread_.join();
      }
    });
  }

  void async_send(const std::vector<uint8_t>& v) {
    auto ptr = std::make_shared<buffer>(v);
    io_service_.post([this, ptr] {
      do_async_send(ptr);
    });
  }

  void async_send(const uint8_t* _Nonnull p, size_t length) {
    auto ptr = std::make_shared<buffer>(p, length);
    io_service_.post([this, ptr] {
      do_async_send(ptr);
    });
  }

private:
  class buffer final {
  public:
    buffer(const std::vector<uint8_t> v) {
      v_ = v;
    }

    buffer(const uint8_t* _Nonnull p, size_t length) {
      v_.resize(length);
      memcpy(&(v_[0]), p, length);
    }

    const std::vector<uint8_t>& get_vector(void) const { return v_; }

  private:
    std::vector<uint8_t> v_;
  };

  void do_async_send(std::shared_ptr<buffer> ptr) {
    socket_.async_send(boost::asio::buffer(ptr->get_vector()),
                       [this, ptr](auto&& error_code,
                                   auto&& bytes_transferred) {
                         if (error_code) {
                           close();
                         }
                       });
  }

  boost::asio::io_service io_service_;
  std::unique_ptr<boost::asio::io_service::work> work_;
  boost::asio::local::datagram_protocol::socket socket_;
  std::thread io_service_thread_;
  bool connected_;

  std::thread heartbeat_thread_;
  std::atomic<bool> heartbeat_enabled_;
};
} // namespace krbn
<|endoftext|>


<commit_before>#include "xchainer/array.h"

#include <algorithm>
#include <cassert>
#include <cstring>
#include <string>
#include <unordered_map>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda.h>
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA
#include <gsl/gsl>

#include "xchainer/array_fill.h"
#include "xchainer/array_math.h"
#include "xchainer/array_repr.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/array_fill.h"
#include "xchainer/cuda/array_math.h"
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"
#include "xchainer/error.h"
#include "xchainer/memory.h"
#include "xchainer/op_node.h"
#include "xchainer/scalar.h"

namespace xchainer {
namespace internal {

// Private definition of ArrayBody
ArrayBody::ArrayBody(const Shape& shape, Dtype dtype, const Device& device, bool is_contiguous, std::shared_ptr<void> data, int64_t offset,
                     std::vector<std::shared_ptr<ArrayNode>> nodes)
    : shape_(shape),
      dtype_(dtype),
      device_(device),
      is_contiguous_(is_contiguous),
      data_(std::move(data)),
      offset_(offset),
      nodes_(std::move(nodes)) {}

void SetUpOpNodes(const std::string& name, const std::vector<std::reference_wrapper<const Array>>& inputs, Array& out,
                  const std::vector<std::function<Array(const Array&, const std::vector<GraphId>&)>>& backward_functions,
                  const std::vector<GraphId>& graph_ids_to_stop_gradients) {
    if (inputs.size() != backward_functions.size()) {
        throw XchainerError("Cannot construct a graph where numbers of input Arrays and backward functions do not match.");
    }

    std::unordered_map<GraphId, std::shared_ptr<OpNode>> graph_edges;

    // Helper function to create an edge in the graph
    auto create_edge = [&name, &graph_edges](const std::shared_ptr<ArrayNode>& next_node, auto& backward_function) {
        std::shared_ptr<OpNode>& op_node = graph_edges[next_node->graph_id()];  // Create if not exists
        if (!op_node) {
            op_node = std::make_shared<OpNode>(name);
        }
        op_node->set_rank(std::max(op_node->rank(), next_node->rank()));
        op_node->RegisterNextNode(next_node, backward_function);
    };

    for (size_t i = 0; i < inputs.size(); ++i) {                                  // For each input
        for (const std::shared_ptr<ArrayNode>& node : inputs[i].get().nodes()) {  // For each graph, create an edge
            if (find(graph_ids_to_stop_gradients.begin(), graph_ids_to_stop_gradients.end(), node->graph_id()) ==
                graph_ids_to_stop_gradients.end()) {
                create_edge(node, backward_functions[i]);
            }
        }
    }

    if (!graph_edges.empty() && std::any_of(inputs.begin(), inputs.end(), [&out](const Array& input) { return &out == &input; })) {
        throw XchainerError("In-place operation (" + name + ") is not supported for an array that require gradients.");
    }

    // Bind edges to output
    for (const auto& edge : graph_edges) {
        const GraphId& graph_id = edge.first;
        const std::shared_ptr<OpNode>& op_node = edge.second;

        const std::shared_ptr<ArrayNode>& out_node = CreateArrayNode(out, graph_id);
        out_node->set_next_node(op_node);
        out_node->set_rank(op_node->rank() + 1);
    }
}

bool HasArrayNode(const Array& array, const GraphId& graph_id) {
    return std::find_if(array.nodes().begin(), array.nodes().end(),
                        [&graph_id](const auto& node) { return graph_id == node->graph_id(); }) != array.nodes().end();
}

const std::shared_ptr<ArrayNode>& CreateArrayNode(Array& array, const GraphId& graph_id) {
    if (HasArrayNode(array, graph_id)) {
        throw XchainerError("Duplicate graph registration: " + graph_id);
    }
    array.nodes().emplace_back(std::make_shared<ArrayNode>(graph_id));
    return array.nodes().back();
}

std::shared_ptr<const ArrayNode> GetArrayNode(const Array& array, const GraphId& graph_id) { return GetMutableArrayNode(array, graph_id); }

const std::shared_ptr<ArrayNode>& GetMutableArrayNode(const Array& array, const GraphId& graph_id) {
    auto it =
        std::find_if(array.nodes().begin(), array.nodes().end(), [&graph_id](const auto& node) { return graph_id == node->graph_id(); });
    if (it == array.nodes().end()) {
        throw XchainerError("Cannot find ArrayNode for graph: " + graph_id);
    }
    return *it;
}

}  // namespace internal

Array::Array(const Shape& shape, Dtype dtype, const Device& device, std::shared_ptr<void> data, bool is_contiguous, int64_t offset)
    : body_(std::make_shared<internal::ArrayBody>(shape, dtype, device, is_contiguous, std::move(data), offset)) {}

Array::Array(const Array& other)
    : body_(std::make_shared<internal::ArrayBody>(other.shape(), other.dtype(), other.device(), other.is_contiguous(), other.body_->data_,
                                                  other.offset(), other.body_->nodes_)) {}

const nonstd::optional<Array>& Array::GetGrad(const GraphId& graph_id) const { return internal::GetArrayNode(*this, graph_id)->grad(); }

void Array::SetGrad(Array grad, const GraphId& graph_id) { internal::GetMutableArrayNode(*this, graph_id)->set_grad(std::move(grad)); }

void Array::ClearGrad(const GraphId& graph_id) { internal::GetMutableArrayNode(*this, graph_id)->ClearGrad(); }

Array Array::FromBuffer(const Shape& shape, Dtype dtype, std::shared_ptr<void> data, const Device& device) {
    auto bytesize = static_cast<size_t>(shape.total_size() * GetElementSize(dtype));
    std::shared_ptr<void> device_data = internal::MemoryFromBuffer(device, data, bytesize);
    return {shape, dtype, device, device_data};
}

Array Array::Empty(const Shape& shape, Dtype dtype, const Device& device) {
    auto bytesize = static_cast<size_t>(shape.total_size() * GetElementSize(dtype));
    std::shared_ptr<void> data = internal::Allocate(device, bytesize);
    return {shape, dtype, device, data};
}

Array Array::Full(const Shape& shape, Scalar scalar, Dtype dtype, const Device& device) {
    Array array = Empty(shape, dtype, device);
    array.Fill(scalar);
    return array;
}

Array Array::Full(const Shape& shape, Scalar scalar, const Device& device) { return Full(shape, scalar, scalar.dtype(), device); }

Array Array::Zeros(const Shape& shape, Dtype dtype, const Device& device) { return Full(shape, 0, dtype, device); }

Array Array::Ones(const Shape& shape, Dtype dtype, const Device& device) { return Full(shape, 1, dtype, device); }

Array Array::EmptyLike(const Array& array, const Device& device) { return Empty(array.shape(), array.dtype(), device); }

Array Array::FullLike(const Array& array, Scalar scalar, const Device& device) {
    return Full(array.shape(), scalar, array.dtype(), device);
}

Array Array::ZerosLike(const Array& array, const Device& device) { return Zeros(array.shape(), array.dtype(), device); }

Array Array::OnesLike(const Array& array, const Device& device) { return Ones(array.shape(), array.dtype(), device); }

Array& Array::operator+=(const Array& rhs) {
    Add(rhs, *this);
    return *this;
}

Array& Array::operator*=(const Array& rhs) {
    Mul(rhs, *this);
    return *this;
}

Array Array::operator+(const Array& rhs) const {
    Array out = Array::EmptyLike(*this);
    Add(rhs, out);
    return out;
}

Array Array::operator*(const Array& rhs) const {
    Array out = Array::EmptyLike(*this);
    Mul(rhs, out);
    return out;
}

Array Array::Copy() const {
    // No graph will be disconnected.
    return AsConstant({}, CopyKind::kCopy);
}

Array Array::AsConstant(CopyKind kind) const {
    switch (kind) {
        case CopyKind::kCopy: {
            Array out = Array::EmptyLike(*this);
            // TODO(takagi): When non-C-contiguous orders are supported, we cannot blindly copy all elements but need to take
            // is_contiguous_ and offset_ into account
            internal::MemoryCopy(out.data().get(), body_->data_.get(), total_bytes());
            return std::move(out);
        }
        case CopyKind::kView:
            return Array{shape(), dtype(), device(), body_->data_, is_contiguous(), offset()};
        default:
            assert(false);  // should never be reached
    }
}

Array Array::AsConstant(const std::vector<GraphId>& disconnect_graph_ids, CopyKind kind) const {
    switch (kind) {
        case CopyKind::kCopy: {
            Array out = Array::EmptyLike(*this);
            internal::SetUpOpNodes("copy", {*this}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout; }},
                                   disconnect_graph_ids);
            // TODO(takagi): When non-C-contiguous orders are supported, we cannot blindly copy all elements but need to take
            // is_contiguous_ and offset_ into account
            internal::MemoryCopy(out.data().get(), body_->data_.get(), total_bytes());
            return std::move(out);
        }
        case CopyKind::kView: {
            Array out{shape(), dtype(), device(), body_->data_, is_contiguous(), offset()};

            // Duplicate the array nodes only when graph IDs are not found in disconnect_graph_ids.
            const std::vector<GraphId>& graph_ids = disconnect_graph_ids;
            for (const std::shared_ptr<ArrayNode>& node : nodes()) {
                if (std::find(graph_ids.begin(), graph_ids.end(), node->graph_id()) == graph_ids.end()) {
                    out.body_->nodes_.emplace_back(node);
                }
            }
            return std::move(out);
        }
        default:
            assert(false);  // should never be reached
    }
}

void Array::Add(const Array& rhs, Array& out) const {
    // TODO(sonots): dtype conversion
    CheckEqual(dtype(), rhs.dtype());
    // TODO(sonots): broadcasting
    CheckEqual(shape(), rhs.shape());

    auto lhs_backward_function = [](const Array& gout, const std::vector<GraphId>&) -> Array { return gout; };
    auto rhs_backward_function = lhs_backward_function;
    internal::SetUpOpNodes("add", {*this, rhs}, out, {lhs_backward_function, rhs_backward_function});

    Device device = GetCurrentDevice();
    // TODO(sonots): Use device.backend->Add()
    if (device.name() == "cpu") {
        xchainer::Add(*this, rhs, out);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device.name() == "cuda") {
        xchainer::cuda::Add(*this, rhs, out);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

void Array::Mul(const Array& rhs, Array& out) const {
    // TODO(sonots): dtype conversion
    CheckEqual(dtype(), rhs.dtype());
    // TODO(sonots): broadcasting
    CheckEqual(shape(), rhs.shape());

    auto lhs_backward_function = [other = rhs](const Array& gout, const std::vector<GraphId>& graph_ids_to_stop_gradient) {
        return gout * other.AsConstant(graph_ids_to_stop_gradient);
    };
    auto rhs_backward_function = [other = *this](const Array& gout, const std::vector<GraphId>& graph_ids_to_stop_gradient) {
        return gout * other.AsConstant(graph_ids_to_stop_gradient);
    };
    internal::SetUpOpNodes("mul", {*this, rhs}, out, {lhs_backward_function, rhs_backward_function});

    Device device = GetCurrentDevice();
    // TODO(sonots): Use device.backend->Mul()
    if (device.name() == "cpu") {
        xchainer::Mul(*this, rhs, out);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device.name() == "cuda") {
        xchainer::cuda::Mul(*this, rhs, out);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

void Array::Fill(Scalar value) {
    // TODO(sonots): Use device.backend->Fill()
    if (device().name() == "cpu") {
        xchainer::Fill(*this, value);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device().name() == "cuda") {
        xchainer::cuda::Fill(*this, value);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

std::string Array::ToString() const { return ArrayRepr(*this); }

namespace {

void DebugDumpComputationalGraph(std::ostream& os, const ArrayNode& array_node, int indent) {
    static const char kIndentChar = ' ';

    os << std::string(static_cast<size_t>(indent * 2), kIndentChar) << "ArrayNode<" << &array_node << ">" << std::endl;

    std::shared_ptr<const OpNode> op = array_node.next_node();
    if (op) {
        os << std::string(static_cast<size_t>((indent + 1) * 2), kIndentChar) << "Op<" << op->name() << "," << op.get() << ">" << std::endl;
        for (const std::shared_ptr<const ArrayNode>& next_node : op->next_nodes()) {
            DebugDumpComputationalGraph(os, *next_node, static_cast<size_t>(indent + 2));
        }
    }
}

}  // namespace

void DebugDumpComputationalGraph(std::ostream& os, const Array& array, const GraphId& graph_id, int indent) {
    DebugDumpComputationalGraph(os, *internal::GetArrayNode(array, graph_id), indent);
}

}  // namespace xchainer
<commit_msg>Change temporary variable name<commit_after>#include "xchainer/array.h"

#include <algorithm>
#include <cassert>
#include <cstring>
#include <string>
#include <unordered_map>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda.h>
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA
#include <gsl/gsl>

#include "xchainer/array_fill.h"
#include "xchainer/array_math.h"
#include "xchainer/array_repr.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/array_fill.h"
#include "xchainer/cuda/array_math.h"
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"
#include "xchainer/error.h"
#include "xchainer/memory.h"
#include "xchainer/op_node.h"
#include "xchainer/scalar.h"

namespace xchainer {
namespace internal {

// Private definition of ArrayBody
ArrayBody::ArrayBody(const Shape& shape, Dtype dtype, const Device& device, bool is_contiguous, std::shared_ptr<void> data, int64_t offset,
                     std::vector<std::shared_ptr<ArrayNode>> nodes)
    : shape_(shape),
      dtype_(dtype),
      device_(device),
      is_contiguous_(is_contiguous),
      data_(std::move(data)),
      offset_(offset),
      nodes_(std::move(nodes)) {}

void SetUpOpNodes(const std::string& name, const std::vector<std::reference_wrapper<const Array>>& inputs, Array& out,
                  const std::vector<std::function<Array(const Array&, const std::vector<GraphId>&)>>& backward_functions,
                  const std::vector<GraphId>& graph_ids_to_stop_gradients) {
    if (inputs.size() != backward_functions.size()) {
        throw XchainerError("Cannot construct a graph where numbers of input Arrays and backward functions do not match.");
    }

    std::unordered_map<GraphId, std::shared_ptr<OpNode>> graph_edges;

    // Helper function to create an edge in the graph
    auto create_edge = [&name, &graph_edges](const std::shared_ptr<ArrayNode>& next_node, auto& backward_function) {
        std::shared_ptr<OpNode>& op_node = graph_edges[next_node->graph_id()];  // Create if not exists
        if (!op_node) {
            op_node = std::make_shared<OpNode>(name);
        }
        op_node->set_rank(std::max(op_node->rank(), next_node->rank()));
        op_node->RegisterNextNode(next_node, backward_function);
    };

    for (size_t i = 0; i < inputs.size(); ++i) {                                  // For each input
        for (const std::shared_ptr<ArrayNode>& node : inputs[i].get().nodes()) {  // For each graph, create an edge
            if (find(graph_ids_to_stop_gradients.begin(), graph_ids_to_stop_gradients.end(), node->graph_id()) ==
                graph_ids_to_stop_gradients.end()) {
                create_edge(node, backward_functions[i]);
            }
        }
    }

    if (!graph_edges.empty() && std::any_of(inputs.begin(), inputs.end(), [&out](const Array& input) { return &out == &input; })) {
        throw XchainerError("In-place operation (" + name + ") is not supported for an array that require gradients.");
    }

    // Bind edges to output
    for (const auto& edge : graph_edges) {
        const GraphId& graph_id = edge.first;
        const std::shared_ptr<OpNode>& op_node = edge.second;

        const std::shared_ptr<ArrayNode>& out_node = CreateArrayNode(out, graph_id);
        out_node->set_next_node(op_node);
        out_node->set_rank(op_node->rank() + 1);
    }
}

bool HasArrayNode(const Array& array, const GraphId& graph_id) {
    return std::find_if(array.nodes().begin(), array.nodes().end(),
                        [&graph_id](const auto& node) { return graph_id == node->graph_id(); }) != array.nodes().end();
}

const std::shared_ptr<ArrayNode>& CreateArrayNode(Array& array, const GraphId& graph_id) {
    if (HasArrayNode(array, graph_id)) {
        throw XchainerError("Duplicate graph registration: " + graph_id);
    }
    array.nodes().emplace_back(std::make_shared<ArrayNode>(graph_id));
    return array.nodes().back();
}

std::shared_ptr<const ArrayNode> GetArrayNode(const Array& array, const GraphId& graph_id) { return GetMutableArrayNode(array, graph_id); }

const std::shared_ptr<ArrayNode>& GetMutableArrayNode(const Array& array, const GraphId& graph_id) {
    auto it =
        std::find_if(array.nodes().begin(), array.nodes().end(), [&graph_id](const auto& node) { return graph_id == node->graph_id(); });
    if (it == array.nodes().end()) {
        throw XchainerError("Cannot find ArrayNode for graph: " + graph_id);
    }
    return *it;
}

}  // namespace internal

Array::Array(const Shape& shape, Dtype dtype, const Device& device, std::shared_ptr<void> data, bool is_contiguous, int64_t offset)
    : body_(std::make_shared<internal::ArrayBody>(shape, dtype, device, is_contiguous, std::move(data), offset)) {}

Array::Array(const Array& other)
    : body_(std::make_shared<internal::ArrayBody>(other.shape(), other.dtype(), other.device(), other.is_contiguous(), other.body_->data_,
                                                  other.offset(), other.body_->nodes_)) {}

const nonstd::optional<Array>& Array::GetGrad(const GraphId& graph_id) const { return internal::GetArrayNode(*this, graph_id)->grad(); }

void Array::SetGrad(Array grad, const GraphId& graph_id) { internal::GetMutableArrayNode(*this, graph_id)->set_grad(std::move(grad)); }

void Array::ClearGrad(const GraphId& graph_id) { internal::GetMutableArrayNode(*this, graph_id)->ClearGrad(); }

Array Array::FromBuffer(const Shape& shape, Dtype dtype, std::shared_ptr<void> data, const Device& device) {
    auto bytesize = static_cast<size_t>(shape.total_size() * GetElementSize(dtype));
    std::shared_ptr<void> device_data = internal::MemoryFromBuffer(device, data, bytesize);
    return {shape, dtype, device, device_data};
}

Array Array::Empty(const Shape& shape, Dtype dtype, const Device& device) {
    auto bytesize = static_cast<size_t>(shape.total_size() * GetElementSize(dtype));
    std::shared_ptr<void> data = internal::Allocate(device, bytesize);
    return {shape, dtype, device, data};
}

Array Array::Full(const Shape& shape, Scalar scalar, Dtype dtype, const Device& device) {
    Array array = Empty(shape, dtype, device);
    array.Fill(scalar);
    return array;
}

Array Array::Full(const Shape& shape, Scalar scalar, const Device& device) { return Full(shape, scalar, scalar.dtype(), device); }

Array Array::Zeros(const Shape& shape, Dtype dtype, const Device& device) { return Full(shape, 0, dtype, device); }

Array Array::Ones(const Shape& shape, Dtype dtype, const Device& device) { return Full(shape, 1, dtype, device); }

Array Array::EmptyLike(const Array& array, const Device& device) { return Empty(array.shape(), array.dtype(), device); }

Array Array::FullLike(const Array& array, Scalar scalar, const Device& device) {
    return Full(array.shape(), scalar, array.dtype(), device);
}

Array Array::ZerosLike(const Array& array, const Device& device) { return Zeros(array.shape(), array.dtype(), device); }

Array Array::OnesLike(const Array& array, const Device& device) { return Ones(array.shape(), array.dtype(), device); }

Array& Array::operator+=(const Array& rhs) {
    Add(rhs, *this);
    return *this;
}

Array& Array::operator*=(const Array& rhs) {
    Mul(rhs, *this);
    return *this;
}

Array Array::operator+(const Array& rhs) const {
    Array out = Array::EmptyLike(*this);
    Add(rhs, out);
    return out;
}

Array Array::operator*(const Array& rhs) const {
    Array out = Array::EmptyLike(*this);
    Mul(rhs, out);
    return out;
}

Array Array::Copy() const {
    // No graph will be disconnected.
    return AsConstant({}, CopyKind::kCopy);
}

Array Array::AsConstant(CopyKind kind) const {
    switch (kind) {
        case CopyKind::kCopy: {
            Array out = Array::EmptyLike(*this);
            // TODO(takagi): When non-C-contiguous orders are supported, we cannot blindly copy all elements but need to take
            // is_contiguous_ and offset_ into account
            internal::MemoryCopy(out.data().get(), body_->data_.get(), total_bytes());
            return std::move(out);
        }
        case CopyKind::kView:
            return Array{shape(), dtype(), device(), body_->data_, is_contiguous(), offset()};
        default:
            assert(false);  // should never be reached
    }
}

Array Array::AsConstant(const std::vector<GraphId>& disconnect_graph_ids, CopyKind kind) const {
    switch (kind) {
        case CopyKind::kCopy: {
            Array out = Array::EmptyLike(*this);
            internal::SetUpOpNodes("copy", {*this}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout; }},
                                   disconnect_graph_ids);
            // TODO(takagi): When non-C-contiguous orders are supported, we cannot blindly copy all elements but need to take
            // is_contiguous_ and offset_ into account
            internal::MemoryCopy(out.data().get(), body_->data_.get(), total_bytes());
            return std::move(out);
        }
        case CopyKind::kView: {
            Array out{shape(), dtype(), device(), body_->data_, is_contiguous(), offset()};

            // Duplicate the array nodes only when graph IDs are not found in disconnect_graph_ids.
            const std::vector<GraphId>& dgs = disconnect_graph_ids;
            for (const std::shared_ptr<ArrayNode>& node : nodes()) {
                if (std::find(dgs.begin(), dgs.end(), node->graph_id()) == dgs.end()) {
                    // extend the graph
                    out.body_->nodes_.emplace_back(node);
                }
            }
            return std::move(out);
        }
        default:
            assert(false);  // should never be reached
    }
}

void Array::Add(const Array& rhs, Array& out) const {
    // TODO(sonots): dtype conversion
    CheckEqual(dtype(), rhs.dtype());
    // TODO(sonots): broadcasting
    CheckEqual(shape(), rhs.shape());

    auto lhs_backward_function = [](const Array& gout, const std::vector<GraphId>&) -> Array { return gout; };
    auto rhs_backward_function = lhs_backward_function;
    internal::SetUpOpNodes("add", {*this, rhs}, out, {lhs_backward_function, rhs_backward_function});

    Device device = GetCurrentDevice();
    // TODO(sonots): Use device.backend->Add()
    if (device.name() == "cpu") {
        xchainer::Add(*this, rhs, out);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device.name() == "cuda") {
        xchainer::cuda::Add(*this, rhs, out);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

void Array::Mul(const Array& rhs, Array& out) const {
    // TODO(sonots): dtype conversion
    CheckEqual(dtype(), rhs.dtype());
    // TODO(sonots): broadcasting
    CheckEqual(shape(), rhs.shape());

    auto lhs_backward_function = [other = rhs](const Array& gout, const std::vector<GraphId>& graph_ids_to_stop_gradient) {
        return gout * other.AsConstant(graph_ids_to_stop_gradient);
    };
    auto rhs_backward_function = [other = *this](const Array& gout, const std::vector<GraphId>& graph_ids_to_stop_gradient) {
        return gout * other.AsConstant(graph_ids_to_stop_gradient);
    };
    internal::SetUpOpNodes("mul", {*this, rhs}, out, {lhs_backward_function, rhs_backward_function});

    Device device = GetCurrentDevice();
    // TODO(sonots): Use device.backend->Mul()
    if (device.name() == "cpu") {
        xchainer::Mul(*this, rhs, out);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device.name() == "cuda") {
        xchainer::cuda::Mul(*this, rhs, out);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

void Array::Fill(Scalar value) {
    // TODO(sonots): Use device.backend->Fill()
    if (device().name() == "cpu") {
        xchainer::Fill(*this, value);
#ifdef XCHAINER_ENABLE_CUDA
    } else if (device().name() == "cuda") {
        xchainer::cuda::Fill(*this, value);
#endif  // XCHAINER_ENABLE_CUDA
    } else {
        throw DeviceError("invalid device");
    }
}

std::string Array::ToString() const { return ArrayRepr(*this); }

namespace {

void DebugDumpComputationalGraph(std::ostream& os, const ArrayNode& array_node, int indent) {
    static const char kIndentChar = ' ';

    os << std::string(static_cast<size_t>(indent * 2), kIndentChar) << "ArrayNode<" << &array_node << ">" << std::endl;

    std::shared_ptr<const OpNode> op = array_node.next_node();
    if (op) {
        os << std::string(static_cast<size_t>((indent + 1) * 2), kIndentChar) << "Op<" << op->name() << "," << op.get() << ">" << std::endl;
        for (const std::shared_ptr<const ArrayNode>& next_node : op->next_nodes()) {
            DebugDumpComputationalGraph(os, *next_node, static_cast<size_t>(indent + 2));
        }
    }
}

}  // namespace

void DebugDumpComputationalGraph(std::ostream& os, const Array& array, const GraphId& graph_id, int indent) {
    DebugDumpComputationalGraph(os, *internal::GetArrayNode(array, graph_id), indent);
}

}  // namespace xchainer
<|endoftext|>