root/src/apps/cortex/NodeManager/NodeSyncThread.cpp
/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


// NodeSyncThread.cpp

#include "NodeSyncThread.h"

#include <cstring>

#include <Debug.h>
#include <MediaRoster.h>

__USE_CORTEX_NAMESPACE

// -------------------------------------------------------- //
// *** dtor/ctors
// -------------------------------------------------------- //

NodeSyncThread::~NodeSyncThread() {
        status_t err;

        // clean up
        if(m_thread) {
                err = kill_thread(m_thread);
                if(err < B_OK)
                        PRINT((
                                "! ~NodeSyncThread(): kill_thread(%" B_PRId32 "):\n"
                                "  %s\n",
                                m_thread,
                                strerror(err)));
                                
                // +++++ is a wait_for_thread() necessary?
        }
        
        if(m_port) {
                err = delete_port(m_port);
                if(err < B_OK)
                        PRINT((
                                "! ~NodeSyncThread(): delete_port(%" B_PRId32 "):\n"
                                "  %s\n",
                                m_port,
                                strerror(err)));
        }
        
        if(m_portBuffer)
                delete [] m_portBuffer;
        if(m_messenger)
                delete m_messenger;
}

NodeSyncThread::NodeSyncThread(
        const media_node&                                               node,
        BMessenger*                                                                     messenger) :
        
        m_node(node),
        m_messenger(messenger),
        m_syncInProgress(false),
        m_thread(0),
        m_port(0),
        m_portBuffer(0),
        m_portBufferSize(sizeof(_sync_op)) {            
        
        ASSERT(m_messenger);

        m_portBuffer = new char[m_portBufferSize];

        m_port = create_port(
                m_portBufferSize * 16,
                "NodeSyncThread___port");
        ASSERT(m_port >= B_OK);

        m_thread = spawn_thread(
                &_Sync,
                "NodeSyncThread",
                B_DISPLAY_PRIORITY,
                this);
        ASSERT(m_thread >= B_OK);
        resume_thread(m_thread);
}       

// -------------------------------------------------------- //
// *** operations
// -------------------------------------------------------- //

// trigger a sync operation: when 'perfTime' arrives
// for the node, a M_SYNC_COMPLETE message with the given
// position value will be sent,  unless the sync operation
// times out, in which case M_TIMED_OUT will be sent.

status_t NodeSyncThread::sync(
        bigtime_t                                                                               perfTime,
        bigtime_t                                                                               position,
        bigtime_t                                                                               timeout) {

        status_t err;
        
        if(m_syncInProgress)
                return B_NOT_ALLOWED;   

        _sync_op op = {perfTime, position, timeout};
        err = write_port(
                m_port,
                M_TRIGGER,
                &op,
                sizeof(_sync_op));
        
        return err;
}

// -------------------------------------------------------- //
// *** guts
// -------------------------------------------------------- //

/*static*/
status_t NodeSyncThread::_Sync(
        void*                                                                                           cookie) {
        ((NodeSyncThread*)cookie)->_sync();
        return B_OK;
}

// THREAD BODY
//
void NodeSyncThread::_sync() {
        ASSERT(m_port >= B_OK);
        ASSERT(m_messenger);

        bool done = false;
        while(!done) {
        
                // WAIT FOR A REQUEST
                int32 code;
                ssize_t readCount = read_port(  
                        m_port,
                        &code,
                        m_portBuffer,
                        m_portBufferSize);
                        
                if(readCount < B_OK) {
                        PRINT((
                                "! NodeSyncThread::_sync(): read_port():\n"
                                "  %s\n",
                                strerror(readCount)));
                        continue;
                }
                
                if(code != M_TRIGGER) {
                        PRINT((
                                "! NodeSyncThread::sync(): unknown message code %" B_PRId32
                                        "\n", code));
                        continue;
                }
                
                // SERVICE THE REQUEST
                const _sync_op& op = *(_sync_op*)m_portBuffer;
                
                // pre-fill the message
                BMessage m(M_SYNC_COMPLETE);
                m.AddInt32("nodeID", m_node.node);
                m.AddInt64("perfTime", op.targetTime);
                m.AddInt64("position", op.position);

                // sync
                status_t err = BMediaRoster::Roster()->SyncToNode(
                        m_node,
                        op.targetTime,
                        op.timeout);

                // deliver reply
                if(err < B_OK) {
                        m.what = M_SYNC_FAILED;
                        m.AddInt32("error", err);
                }
                
                err = m_messenger->SendMessage(&m);
                if(err < B_OK) {
                        PRINT((
                                "! NodeSyncThread::_sync(): m_messenger->SendMessage():\n"
                                "  %s\n",
                                strerror(err)));
                }
        }
}


// END -- NodeSyncThread.cpp --