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

#include "Fass.h"

// #include <stdlib.h>
// #include <libxml/parser.h>
// #include <fcntl.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <iostream>

// #include "Log.h"
// #include "RPCManager.h"
// #include "PriorityManager.h"
#include "FassLog.h"
#include "Configurator.h"
#include "ObjectXML.h"
#include "FassDb.h"

void Fass::start(bool bootstrap_only) {
    bool             rc;
    // int             fd;
    sigset_t        mask;
    int             signal;
    char            hn[80];

    /// returns the null-terminated hostname in the character
    /// array hn, which has a length of 79 bytes.
    /// is hostname used somewhere?
    if ( gethostname(hn, 79) != 0 ) {
        throw runtime_error("Error getting hostname");
    }

    hostname = hn;

    /** Configuration system */
    fass_configuration = new FassConfigurator(etc_location, var_location);

    rc = fass_configuration->load_configuration();

    if ( !rc ) {
        throw runtime_error("Could not load Fass configuration file.");
    }

    /** Initial shares system */
    // Read initial shares from separate file
    initial_shares = new SharesConfigurator(etc_location);

    rc = initial_shares->load_shares();

    if ( !rc ) {
        throw runtime_error("Could not load initial shares file.");
    }

    /** Log system */

    ostringstream os;

    try {
        Log::MessageType   clevel;

        clevel     = get_debug_level();

        /// Initializing FASS daemon log system
        string log_fname;
        log_fname = log_location + "fass.log";
        FassLog::init_log_system(clevel,
                           log_fname.c_str(),
                           ios_base::trunc,
                           "fassd");

        os << "Starting " << version() << endl;
        os << "----------------------------------------\n";
        os << "     Fass Configuration File      \n";
        os << "----------------------------------------\n";
        os << fass_configuration->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS", Log::INFO, os);
        fass_configuration->print_loaded_options();

        os.clear();
        os.str("");
        os << "\n";
        os << "----------------------------------------\n";
        os << "     Initial Shares File      \n";
        os << "----------------------------------------\n";
        os << initial_shares->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS", Log::INFO, os);
        initial_shares->print_shares();
    } catch(runtime_error&) {
        throw;
    }

    /** Database */

    // instance of specific DB (can be changed)
    string dbtype;
    string dbendpoint;
    string dbname;
    int dbport;
    fass_configuration->get_single_option("database", "type", dbtype);
    fass_configuration->get_single_option("database", "endpoint", dbendpoint);
    fass_configuration->get_single_option("database", "port", dbport);
    fass_configuration->get_single_option("database", "name", dbname);

    // TODO(svallero): should be done with a switch and enum, not critical
    if ( dbtype == "influxdb" ) {
        database = new InfluxDb(dbendpoint, dbport, dbname);
    } else {
       FassLog::log("FASS", Log::ERROR, "Unknown database type!");
       throw;
    }

    /** Managers */
    // some config variables are used both by the RPCManager
    // and the XMLRPCClient (called by the PM)
    // we use the same valuse for both server/client,
    // these should be consistent with the OpenNebula ones
    int64_t  message_size;
    int  timeout;
    int  manager_timer;  // used also by Terminator
    // OpenNebula xml-rpc endpoint
    string one_endpoint;
    string one_port;
    fass_configuration->get_single_option("fass", "one_endpoint", one_endpoint);
    fass_configuration->get_single_option("fass", "one_port", one_port);
    one_endpoint.append(":");
    one_endpoint.append(one_port);
    one_endpoint.append("/RPC2");
    // FassLog::log("FASS", Log::DEBUG, one_endpoint);
    // OpenNebula authentication
    string one_secret;
    fass_configuration->get_single_option("fass", "one_secret", one_secret);
    // xml-rpc config
    fass_configuration->get_single_option("rpcm", "message_size", message_size);
    fass_configuration->get_single_option("rpcm", "timeout", timeout);
    fass_configuration->get_single_option
         ("pm", "manager_timer", manager_timer);

    // Wait for ONE to become available
    while (1) {
        try {
            xmlrpc_c::value result;
            vector<xmlrpc_c::value> values;
            xmlrpc_c::paramList plist;
            // temporary client
            XMLRPCClient::initialize(one_secret, one_endpoint, message_size,
                                                                   timeout);

            XMLRPCClient *client = XMLRPCClient::client();

            client->call("one.system.config", plist, &result);

            values = xmlrpc_c::value_array(result).vectorValueValue();

            bool   success = xmlrpc_c::value_boolean(values[0]);
            string message = xmlrpc_c::value_string(values[1]);

            if ( !success ) {
                ostringstream oss;
                oss << "Cannot contact oned, will retry... Error: " << message;
                FassLog::log("FASS", Log::ERROR, oss);
            } else {
                // take manager timer from ONE and compare it to PM timer
                // ObjectXML * conf = new ObjectXML();
                // conf->cleanup();
                // conf->xml_parse(message);
                // int one_timer;
                // conf->xpath(one_timer, "/TEMPLATE/MANAGER_TIMER", 0);
                // if (manager_timer > one_timer) {
                //     manager_timer = one_timer - 1;
                //     ostringstream oss;
                //     oss
                //      << "PM timer is shorter than ONE timer, setting it to: "
                //                                             << manager_timer;
                //     FassLog::log("FASS", Log::WARNING, oss);
                // }
                // delete conf;
                break;
            }
        } catch (exception const& e) {
            ostringstream oss;
            oss << "Cannot contact oned, will retry... Error: " << e.what();
            FassLog::log("FASS", Log::ERROR, oss);
        }
        sleep(2);
    }

    FassLog::log("FASS", Log::INFO, "ONE successfully contacted.");
    FassLog::log("FASS", Log::INFO,
          "Make sure than the PM timer is not longer than ONE SCHED_INTERVAL!");

    /** Block all signals before creating any thread */

    sigfillset(&mask);

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    /** Initialize the XML library */
    xmlInitParser();

    /// ---- Priority Manager ----
    vector<string> shares;
    try {
        // int machines_limit;
        int period;
        int n_periods;
        int plugin_debug;
        string start_time;

        // fass_configuration->get_single_option
        //      ("pm", "max_vm", machines_limit);
        fass_configuration->get_single_option
             ("pm", "start_time", start_time);
        fass_configuration->get_single_option
             ("pm", "period", period);
        fass_configuration->get_single_option
             ("pm", "n_periods", n_periods);
        fass_configuration->get_single_option
             ("pm", "plugin_debug", plugin_debug);

        // Get the initial shares vector
        shares = initial_shares->get_shares();

        // for(vector<string>::const_iterator i = shares.begin();
        //                                   i != shares.end(); ++i) {
        //   FassLog::log("CICCIA", Log::INFO, * i);
        // }

        pm = new PriorityManager(one_endpoint, one_secret, message_size,
                   timeout, shares, manager_timer, start_time, database,
                                       period, n_periods, plugin_debug);
        }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating Priority Manager");
        throw;
    }

    /// ---- Start the Priority Manager ----

    rc = pm->start();

    if ( !rc ) {
       throw runtime_error("Could not start the Priority Manager");
    }

    /// ---- Terminator ----
    try {
        // int64_t ttl;
        int64_t max_wait;
        string action;
        fass_configuration->get_single_option
             ("terminator", "manager_timer", manager_timer);
        // fass_configuration->get_single_option
        //      ("terminator", "ttl", ttl);
        fass_configuration->get_single_option
             ("terminator", "max_wait", max_wait);
        fass_configuration->get_single_option
             ("terminator", "action", action);

        // tm = new Terminator(one_endpoint, one_secret, message_size,
        //                        timeout, manager_timer, shares, ttl, max_wait, action);
        tm = new Terminator(one_endpoint, one_secret, message_size,
                            timeout, manager_timer, shares, max_wait, action);
        }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating Terminator");
        throw;
    }

    /// ---- Start Terminator ----

     rc = tm->start();

    if ( !rc ) {
       throw runtime_error("Could not start Terminator");
    }

    /// ---- Request Manager ----
    try {
        // int  rm_port = 0;
        string rm_port = "";
        int  max_conn;
        int  max_conn_backlog;
        int  keepalive_timeout;
        int  keepalive_max_conn;
        bool rpc_log;
        string log_call_format;
        string rpc_filename = "";
        string rm_listen_address;  // = "0.0.0.0";

        fass_configuration->get_single_option
             ("rpcm", "listen_port", rm_port);
        fass_configuration->get_single_option
             ("rpcm", "listen_address", rm_listen_address);
        fass_configuration->get_single_option
             ("rpcm", "max_conn", max_conn);
        fass_configuration->get_single_option
             ("rpcm", "max_conn_backlog", max_conn_backlog);
        fass_configuration->get_single_option
             ("rpcm", "keepalive_timeout", keepalive_timeout);
        fass_configuration->get_single_option
             ("rpcm", "keepalive_max_conn", keepalive_max_conn);
        fass_configuration->get_single_option
             ("rpcm", "rpc_log", rpc_log);
        fass_configuration->get_single_option
             ("rpcm", "log_call_format", log_call_format);

        if (rpc_log) {
            rpc_filename = log_location + "fass_xmlrpc.log";
        }

        rpcm = new RPCManager(one_endpoint, rm_port, max_conn, max_conn_backlog,
            keepalive_timeout, keepalive_max_conn, timeout, rpc_filename,
            log_call_format, rm_listen_address, message_size);
    }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating RPC Manager");
        throw;
    }

    /// ---- Start the Request Manager ----

    rc = rpcm->start();

    if ( !rc ) {
       throw runtime_error("Could not start the RPC Manager");
    }

    /** Wait for a SIGTERM or SIGINT signal */

    sigemptyset(&mask);

    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);

    sigwait(&mask, &signal);

    /** Stop the managers and free resources */

    pm->finalize();
    tm->finalize();
    rpcm->finalize();

    // sleep to wait drivers???

    pthread_join(rpcm->get_thread_id(), 0);
    pthread_join(pm->get_thread_id(), 0);
    pthread_join(tm->get_thread_id(), 0);

    // XML Library
    xmlCleanupParser();

    FassLog::log("FASS", Log::INFO, "All modules finalized, exiting.\n");

    return;

// error_mad:
// Log::log("FASS", Log::ERROR, "Could not load driver");
// throw runtime_error("Could not load a Fass driver");
}

Log::MessageType Fass::get_debug_level() const {
    Log::MessageType clevel = Log::ERROR;

    int log_level_int;

    fass_configuration->get_single_option("fass", "log_level", log_level_int);

    if ( log_level_int != 0 ) {
        if ( Log::ERROR <= log_level_int && log_level_int <= Log::DDDEBUG ) {
            clevel = static_cast<Log::MessageType>(log_level_int);
        }
    }

    return clevel;
}
<commit_msg>Codestyle fix<commit_after>/**
 * Copyright © 2017 INFN Torino - INDIGO-DataCloud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Fass.h"

// #include <stdlib.h>
// #include <libxml/parser.h>
// #include <fcntl.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <iostream>

// #include "Log.h"
// #include "RPCManager.h"
// #include "PriorityManager.h"
#include "FassLog.h"
#include "Configurator.h"
#include "ObjectXML.h"
#include "FassDb.h"

void Fass::start(bool bootstrap_only) {
    bool             rc;
    // int             fd;
    sigset_t        mask;
    int             signal;
    char            hn[80];

    /// returns the null-terminated hostname in the character
    /// array hn, which has a length of 79 bytes.
    /// is hostname used somewhere?
    if ( gethostname(hn, 79) != 0 ) {
        throw runtime_error("Error getting hostname");
    }

    hostname = hn;

    /** Configuration system */
    fass_configuration = new FassConfigurator(etc_location, var_location);

    rc = fass_configuration->load_configuration();

    if ( !rc ) {
        throw runtime_error("Could not load Fass configuration file.");
    }

    /** Initial shares system */
    // Read initial shares from separate file
    initial_shares = new SharesConfigurator(etc_location);

    rc = initial_shares->load_shares();

    if ( !rc ) {
        throw runtime_error("Could not load initial shares file.");
    }

    /** Log system */

    ostringstream os;

    try {
        Log::MessageType   clevel;

        clevel     = get_debug_level();

        /// Initializing FASS daemon log system
        string log_fname;
        log_fname = log_location + "fass.log";
        FassLog::init_log_system(clevel,
                           log_fname.c_str(),
                           ios_base::trunc,
                           "fassd");

        os << "Starting " << version() << endl;
        os << "----------------------------------------\n";
        os << "     Fass Configuration File      \n";
        os << "----------------------------------------\n";
        os << fass_configuration->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS", Log::INFO, os);
        fass_configuration->print_loaded_options();

        os.clear();
        os.str("");
        os << "\n";
        os << "----------------------------------------\n";
        os << "     Initial Shares File      \n";
        os << "----------------------------------------\n";
        os << initial_shares->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS", Log::INFO, os);
        initial_shares->print_shares();
    } catch(runtime_error&) {
        throw;
    }

    /** Database */

    // instance of specific DB (can be changed)
    string dbtype;
    string dbendpoint;
    string dbname;
    int dbport;
    fass_configuration->get_single_option("database", "type", dbtype);
    fass_configuration->get_single_option("database", "endpoint", dbendpoint);
    fass_configuration->get_single_option("database", "port", dbport);
    fass_configuration->get_single_option("database", "name", dbname);

    // TODO(svallero): should be done with a switch and enum, not critical
    if ( dbtype == "influxdb" ) {
        database = new InfluxDb(dbendpoint, dbport, dbname);
    } else {
       FassLog::log("FASS", Log::ERROR, "Unknown database type!");
       throw;
    }

    /** Managers */
    // some config variables are used both by the RPCManager
    // and the XMLRPCClient (called by the PM)
    // we use the same valuse for both server/client,
    // these should be consistent with the OpenNebula ones
    int64_t  message_size;
    int  timeout;
    int  manager_timer;  // used also by Terminator
    // OpenNebula xml-rpc endpoint
    string one_endpoint;
    string one_port;
    fass_configuration->get_single_option("fass", "one_endpoint", one_endpoint);
    fass_configuration->get_single_option("fass", "one_port", one_port);
    one_endpoint.append(":");
    one_endpoint.append(one_port);
    one_endpoint.append("/RPC2");
    // FassLog::log("FASS", Log::DEBUG, one_endpoint);
    // OpenNebula authentication
    string one_secret;
    fass_configuration->get_single_option("fass", "one_secret", one_secret);
    // xml-rpc config
    fass_configuration->get_single_option("rpcm", "message_size", message_size);
    fass_configuration->get_single_option("rpcm", "timeout", timeout);
    fass_configuration->get_single_option
         ("pm", "manager_timer", manager_timer);

    // Wait for ONE to become available
    while (1) {
        try {
            xmlrpc_c::value result;
            vector<xmlrpc_c::value> values;
            xmlrpc_c::paramList plist;
            // temporary client
            XMLRPCClient::initialize(one_secret, one_endpoint, message_size,
                                                                   timeout);

            XMLRPCClient *client = XMLRPCClient::client();

            client->call("one.system.config", plist, &result);

            values = xmlrpc_c::value_array(result).vectorValueValue();

            bool   success = xmlrpc_c::value_boolean(values[0]);
            string message = xmlrpc_c::value_string(values[1]);

            if ( !success ) {
                ostringstream oss;
                oss << "Cannot contact oned, will retry... Error: " << message;
                FassLog::log("FASS", Log::ERROR, oss);
            } else {
                // take manager timer from ONE and compare it to PM timer
                // ObjectXML * conf = new ObjectXML();
                // conf->cleanup();
                // conf->xml_parse(message);
                // int one_timer;
                // conf->xpath(one_timer, "/TEMPLATE/MANAGER_TIMER", 0);
                // if (manager_timer > one_timer) {
                //     manager_timer = one_timer - 1;
                //     ostringstream oss;
                //     oss
                //      << "PM timer is shorter than ONE timer, setting it to: "
                //                                             << manager_timer;
                //     FassLog::log("FASS", Log::WARNING, oss);
                // }
                // delete conf;
                break;
            }
        } catch (exception const& e) {
            ostringstream oss;
            oss << "Cannot contact oned, will retry... Error: " << e.what();
            FassLog::log("FASS", Log::ERROR, oss);
        }
        sleep(2);
    }

    FassLog::log("FASS", Log::INFO, "ONE successfully contacted.");
    FassLog::log("FASS", Log::INFO,
          "Make sure than the PM timer is not longer than ONE SCHED_INTERVAL!");

    /** Block all signals before creating any thread */

    sigfillset(&mask);

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    /** Initialize the XML library */
    xmlInitParser();

    /// ---- Priority Manager ----
    vector<string> shares;
    try {
        // int machines_limit;
        int period;
        int n_periods;
        int plugin_debug;
        string start_time;

        // fass_configuration->get_single_option
        //      ("pm", "max_vm", machines_limit);
        fass_configuration->get_single_option
             ("pm", "start_time", start_time);
        fass_configuration->get_single_option
             ("pm", "period", period);
        fass_configuration->get_single_option
             ("pm", "n_periods", n_periods);
        fass_configuration->get_single_option
             ("pm", "plugin_debug", plugin_debug);

        // Get the initial shares vector
        shares = initial_shares->get_shares();

        // for(vector<string>::const_iterator i = shares.begin();
        //                                   i != shares.end(); ++i) {
        //   FassLog::log("CICCIA", Log::INFO, * i);
        // }

        pm = new PriorityManager(one_endpoint, one_secret, message_size,
                   timeout, shares, manager_timer, start_time, database,
                                       period, n_periods, plugin_debug);
        }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating Priority Manager");
        throw;
    }

    /// ---- Start the Priority Manager ----

    rc = pm->start();

    if ( !rc ) {
       throw runtime_error("Could not start the Priority Manager");
    }

    /// ---- Terminator ----
    try {
        // int64_t ttl;
        int64_t max_wait;
        string action;
        fass_configuration->get_single_option
             ("terminator", "manager_timer", manager_timer);
        // fass_configuration->get_single_option
        //      ("terminator", "ttl", ttl);
        fass_configuration->get_single_option
             ("terminator", "max_wait", max_wait);
        fass_configuration->get_single_option
             ("terminator", "action", action);

        tm = new Terminator(one_endpoint, one_secret, message_size,
                            timeout, manager_timer, shares, max_wait, action);
        }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating Terminator");
        throw;
    }

    /// ---- Start Terminator ----

     rc = tm->start();

    if ( !rc ) {
       throw runtime_error("Could not start Terminator");
    }

    /// ---- Request Manager ----
    try {
        // int  rm_port = 0;
        string rm_port = "";
        int  max_conn;
        int  max_conn_backlog;
        int  keepalive_timeout;
        int  keepalive_max_conn;
        bool rpc_log;
        string log_call_format;
        string rpc_filename = "";
        string rm_listen_address;  // = "0.0.0.0";

        fass_configuration->get_single_option
             ("rpcm", "listen_port", rm_port);
        fass_configuration->get_single_option
             ("rpcm", "listen_address", rm_listen_address);
        fass_configuration->get_single_option
             ("rpcm", "max_conn", max_conn);
        fass_configuration->get_single_option
             ("rpcm", "max_conn_backlog", max_conn_backlog);
        fass_configuration->get_single_option
             ("rpcm", "keepalive_timeout", keepalive_timeout);
        fass_configuration->get_single_option
             ("rpcm", "keepalive_max_conn", keepalive_max_conn);
        fass_configuration->get_single_option
             ("rpcm", "rpc_log", rpc_log);
        fass_configuration->get_single_option
             ("rpcm", "log_call_format", log_call_format);

        if (rpc_log) {
            rpc_filename = log_location + "fass_xmlrpc.log";
        }

        rpcm = new RPCManager(one_endpoint, rm_port, max_conn, max_conn_backlog,
            keepalive_timeout, keepalive_max_conn, timeout, rpc_filename,
            log_call_format, rm_listen_address, message_size);
    }

    catch (bad_alloc&) {
        FassLog::log("FASS", Log::ERROR, "Error creating RPC Manager");
        throw;
    }

    /// ---- Start the Request Manager ----

    rc = rpcm->start();

    if ( !rc ) {
       throw runtime_error("Could not start the RPC Manager");
    }

    /** Wait for a SIGTERM or SIGINT signal */

    sigemptyset(&mask);

    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);

    sigwait(&mask, &signal);

    /** Stop the managers and free resources */

    pm->finalize();
    tm->finalize();
    rpcm->finalize();

    // sleep to wait drivers???

    pthread_join(rpcm->get_thread_id(), 0);
    pthread_join(pm->get_thread_id(), 0);
    pthread_join(tm->get_thread_id(), 0);

    // XML Library
    xmlCleanupParser();

    FassLog::log("FASS", Log::INFO, "All modules finalized, exiting.\n");

    return;

// error_mad:
// Log::log("FASS", Log::ERROR, "Could not load driver");
// throw runtime_error("Could not load a Fass driver");
}

Log::MessageType Fass::get_debug_level() const {
    Log::MessageType clevel = Log::ERROR;

    int log_level_int;

    fass_configuration->get_single_option("fass", "log_level", log_level_int);

    if ( log_level_int != 0 ) {
        if ( Log::ERROR <= log_level_int && log_level_int <= Log::DDDEBUG ) {
            clevel = static_cast<Log::MessageType>(log_level_int);
        }
    }

    return clevel;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.

*/

#include "libtorrent/file.hpp"
#include "libtorrent/utf8.hpp"

#include <sstream>
#include <windows.h>

namespace
{
	// must be used to not leak memory in case something would throw
	class auto_localfree
	{
	public:
		auto_localfree(HLOCAL memory)
			: m_memory(memory)
		{
		}
		~auto_localfree()
		{
			if (m_memory)
				LocalFree(m_memory);
		}
	private:
		HLOCAL m_memory;
	};

	std::string utf8_native(std::string const& s)
	{
		try
		{
			std::wstring ws;
			libtorrent::utf8_wchar(s, ws);
			std::size_t size = wcstombs(0, ws.c_str(), 0);
			if (size == std::size_t(-1)) return s;
			std::string ret;
			ret.resize(size);
			size = wcstombs(&ret[0], ws.c_str(), size + 1);
			if (ret == wchar_t(-1)) return s;
			ret.resize(size);
			return ret;
		}
		catch(std::exception)
		{
			return s;
		}
	}

	void throw_exception(const char* thrower)
	{
		DWORD err = GetLastError();

#ifdef UNICODE
		wchar_t *wbuffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
		auto_localfree auto_free(wbuffer);
		std::string tmp_utf8;
		libtorrent::wchar_utf8(wbuffer, tmp_utf8);
		char const* buffer = tmp_utf8.c_str();
#else
		char* buffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPSTR)&buffer, 0, 0);
		auto_localfree auto_free(buffer);
#endif

		std::stringstream s;
		s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");

		throw libtorrent::file_error(s.str());
	}
}

namespace libtorrent
{

	struct file::impl : boost::noncopyable
	{
		enum open_flags
		{
			read_flag = 1,
			write_flag = 2
		};

		enum seek_mode
		{
			seek_begin = FILE_BEGIN,
			seek_from_here = FILE_CURRENT,
			seek_end = FILE_END
		};

		impl()
		{
			m_file_handle = INVALID_HANDLE_VALUE;
		}

		void open(const char *file_name, open_flags flags)
		{
			assert(file_name);
			assert(flags & (read_flag | write_flag));

			DWORD access_mask = 0;
			if (flags & read_flag)
				access_mask |= GENERIC_READ;
			if (flags & write_flag)
				access_mask |= GENERIC_WRITE;

			assert(access_mask & (GENERIC_READ | GENERIC_WRITE));

		#ifdef UNICODE
			std::wstring wfile_name(safe_convert(file_name));
			HANDLE new_handle = CreateFile(
				wfile_name.c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#else
			HANDLE new_handle = CreateFile(
				utf8_native(file_name).c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#endif

			if (new_handle == INVALID_HANDLE_VALUE)
			{
				std::stringstream s;
				throw_exception(file_name);
			}
			// will only close old file if the open succeeded
			close();
			m_file_handle = new_handle;
		}

		void close()
		{
			if (m_file_handle != INVALID_HANDLE_VALUE)
			{
				CloseHandle(m_file_handle);
				m_file_handle = INVALID_HANDLE_VALUE;
			}
		}

		~impl()
		{
			close();
		}

		size_type write(const char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);
			DWORD bytes_written = 0;
			if (num_bytes != 0)
			{
				if (FALSE == WriteFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_written
					, 0))
				{
					throw_exception("file::write");
				}
			}
			return bytes_written;
		}

		size_type read(char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);

			DWORD bytes_read = 0;
			if (num_bytes != 0)
			{
				if (FALSE == ReadFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_read
					, 0))
				{
					throw_exception("file::read");
				}
			}
			return bytes_read;
		}

		void seek(size_type pos, seek_mode from_where)
		{
			assert(pos >= 0 || from_where != seek_begin);
			assert(pos <= 0 || from_where != seek_end);
			LARGE_INTEGER offs;
			offs.QuadPart = pos;
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, from_where))
			{
				throw_exception("file::seek");
			}
		}

		size_type tell()
		{
			LARGE_INTEGER offs;
			offs.QuadPart = 0;

			// is there any other way to get offset?
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, FILE_CURRENT))
			{
				throw_exception("file::tell");
			}

			size_type pos=offs.QuadPart;
			assert(pos>=0);
			return pos;
		}
/*
		size_type size()
		{
			LARGE_INTEGER s;
			if (FALSE == GetFileSizeEx(m_file_handle, &s))
			{
				throw_exception("file::size");
			}

			size_type size = s.QuadPart;
			assert(size >= 0);
			return size;
		}
*/
	private:

		HANDLE m_file_handle;

	};
}

namespace libtorrent
{

	const file::seek_mode file::begin(file::impl::seek_begin);
	const file::seek_mode file::end(file::impl::seek_end);

	const file::open_mode file::in(file::impl::read_flag);
	const file::open_mode file::out(file::impl::write_flag);

	file::file()
		: m_impl(new libtorrent::file::impl())
	{
	}
	file::file(boost::filesystem::path const& p, open_mode m)
		: m_impl(new libtorrent::file::impl())
	{
		open(p,m);
	}

	file::~file()
	{
	}

	void file::open(boost::filesystem::path const& p, open_mode m)
	{
		assert(p.is_complete());
		m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buffer, size_type num_bytes)
	{
		return m_impl->write(buffer, num_bytes);
	}

	size_type file::read(char* buffer, size_type num_bytes)
	{
		return m_impl->read(buffer, num_bytes);
	}

	void file::seek(size_type pos, seek_mode m)
	{
		m_impl->seek(pos,impl::seek_mode(m.m_val));
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}
}
<commit_msg>fix<commit_after>/*

Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.

*/

#include "libtorrent/file.hpp"
#include "libtorrent/utf8.hpp"

#include <sstream>
#include <windows.h>

namespace
{
	// must be used to not leak memory in case something would throw
	class auto_localfree
	{
	public:
		auto_localfree(HLOCAL memory)
			: m_memory(memory)
		{
		}
		~auto_localfree()
		{
			if (m_memory)
				LocalFree(m_memory);
		}
	private:
		HLOCAL m_memory;
	};

	std::string utf8_native(std::string const& s)
	{
		try
		{
			std::wstring ws;
			libtorrent::utf8_wchar(s, ws);
			std::size_t size = wcstombs(0, ws.c_str(), 0);
			if (size == std::size_t(-1)) return s;
			std::string ret;
			ret.resize(size);
			size = wcstombs(&ret[0], ws.c_str(), size + 1);
			if (size == wchar_t(-1)) return s;
			ret.resize(size);
			return ret;
		}
		catch(std::exception)
		{
			return s;
		}
	}

	void throw_exception(const char* thrower)
	{
		DWORD err = GetLastError();

#ifdef UNICODE
		wchar_t *wbuffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
		auto_localfree auto_free(wbuffer);
		std::string tmp_utf8;
		libtorrent::wchar_utf8(wbuffer, tmp_utf8);
		char const* buffer = tmp_utf8.c_str();
#else
		char* buffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPSTR)&buffer, 0, 0);
		auto_localfree auto_free(buffer);
#endif

		std::stringstream s;
		s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");

		throw libtorrent::file_error(s.str());
	}
}

namespace libtorrent
{

	struct file::impl : boost::noncopyable
	{
		enum open_flags
		{
			read_flag = 1,
			write_flag = 2
		};

		enum seek_mode
		{
			seek_begin = FILE_BEGIN,
			seek_from_here = FILE_CURRENT,
			seek_end = FILE_END
		};

		impl()
		{
			m_file_handle = INVALID_HANDLE_VALUE;
		}

		void open(const char *file_name, open_flags flags)
		{
			assert(file_name);
			assert(flags & (read_flag | write_flag));

			DWORD access_mask = 0;
			if (flags & read_flag)
				access_mask |= GENERIC_READ;
			if (flags & write_flag)
				access_mask |= GENERIC_WRITE;

			assert(access_mask & (GENERIC_READ | GENERIC_WRITE));

		#ifdef UNICODE
			std::wstring wfile_name(safe_convert(file_name));
			HANDLE new_handle = CreateFile(
				wfile_name.c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#else
			HANDLE new_handle = CreateFile(
				utf8_native(file_name).c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#endif

			if (new_handle == INVALID_HANDLE_VALUE)
			{
				std::stringstream s;
				throw_exception(file_name);
			}
			// will only close old file if the open succeeded
			close();
			m_file_handle = new_handle;
		}

		void close()
		{
			if (m_file_handle != INVALID_HANDLE_VALUE)
			{
				CloseHandle(m_file_handle);
				m_file_handle = INVALID_HANDLE_VALUE;
			}
		}

		~impl()
		{
			close();
		}

		size_type write(const char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);
			DWORD bytes_written = 0;
			if (num_bytes != 0)
			{
				if (FALSE == WriteFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_written
					, 0))
				{
					throw_exception("file::write");
				}
			}
			return bytes_written;
		}

		size_type read(char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);

			DWORD bytes_read = 0;
			if (num_bytes != 0)
			{
				if (FALSE == ReadFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_read
					, 0))
				{
					throw_exception("file::read");
				}
			}
			return bytes_read;
		}

		void seek(size_type pos, seek_mode from_where)
		{
			assert(pos >= 0 || from_where != seek_begin);
			assert(pos <= 0 || from_where != seek_end);
			LARGE_INTEGER offs;
			offs.QuadPart = pos;
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, from_where))
			{
				throw_exception("file::seek");
			}
		}

		size_type tell()
		{
			LARGE_INTEGER offs;
			offs.QuadPart = 0;

			// is there any other way to get offset?
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, FILE_CURRENT))
			{
				throw_exception("file::tell");
			}

			size_type pos=offs.QuadPart;
			assert(pos>=0);
			return pos;
		}
/*
		size_type size()
		{
			LARGE_INTEGER s;
			if (FALSE == GetFileSizeEx(m_file_handle, &s))
			{
				throw_exception("file::size");
			}

			size_type size = s.QuadPart;
			assert(size >= 0);
			return size;
		}
*/
	private:

		HANDLE m_file_handle;

	};
}

namespace libtorrent
{

	const file::seek_mode file::begin(file::impl::seek_begin);
	const file::seek_mode file::end(file::impl::seek_end);

	const file::open_mode file::in(file::impl::read_flag);
	const file::open_mode file::out(file::impl::write_flag);

	file::file()
		: m_impl(new libtorrent::file::impl())
	{
	}
	file::file(boost::filesystem::path const& p, open_mode m)
		: m_impl(new libtorrent::file::impl())
	{
		open(p,m);
	}

	file::~file()
	{
	}

	void file::open(boost::filesystem::path const& p, open_mode m)
	{
		assert(p.is_complete());
		m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buffer, size_type num_bytes)
	{
		return m_impl->write(buffer, num_bytes);
	}

	size_type file::read(char* buffer, size_type num_bytes)
	{
		return m_impl->read(buffer, num_bytes);
	}

	void file::seek(size_type pos, seek_mode m)
	{
		m_impl->seek(pos,impl::seek_mode(m.m_val));
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.

*/

#include "libtorrent/file.hpp"
#include "libtorrent/utf8.hpp"

#include <sstream>
#include <windows.h>

namespace
{
	// must be used to not leak memory in case something would throw
	class auto_localfree
	{
	public:
		auto_localfree(HLOCAL memory)
			: m_memory(memory)
		{
		}
		~auto_localfree()
		{
			if (m_memory)
				LocalFree(m_memory);
		}
	private:
		HLOCAL m_memory;
	};

	void throw_exception(const char* thrower)
	{
		int err = GetLastError();

#ifdef UNICODE
		wchar_t *wbuffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPWCSTR)&wbuffer, 0, 0);
		auto_localfree auto_free(wbuffer);
		std::string tmp_utf8;
		wchar_utf8(wbuffer, tmp_utf8);
		char* buffer = tmp_utf8.c_str();
#else
		char* buffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPSTR)&buffer, 0, 0);
		auto_localfree auto_free(buffer);
#endif

		std::stringstream s;
		s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");

		throw libtorrent::file_error(s.str());
	}
}

namespace libtorrent
{

	struct file::impl : boost::noncopyable
	{
		enum open_flags
		{
			read_flag = 1,
			write_flag = 2
		};

		enum seek_mode
		{
			seek_begin = FILE_BEGIN,
			seek_from_here = FILE_CURRENT,
			seek_end = FILE_END
		};

		impl()
		{
			m_file_handle = INVALID_HANDLE_VALUE;
		}

		void open(const char *file_name, open_flags flags)
		{
			assert(file_name);
			assert(flags & (read_flag | write_flag));

			DWORD access_mask = 0;
			if (flags & read_flag)
				access_mask |= GENERIC_READ;
			if (flags & write_flag)
				access_mask |= GENERIC_WRITE;

			assert(access_mask & (GENERIC_READ | GENERIC_WRITE));

		#ifdef UNICODE
			std::wstring wfile_name(utf8_wchar(file_name));
			HANDLE new_handle = CreateFile(
				(LPCWSTR)wfile_name.c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#else
			HANDLE new_handle = CreateFile(
				file_name
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#endif

			if (new_handle == INVALID_HANDLE_VALUE)
			{
				std::stringstream s;
				throw_exception(file_name);
			}
			// will only close old file if the open succeeded
			close();
			m_file_handle = new_handle;
		}

		void close()
		{
			if (m_file_handle != INVALID_HANDLE_VALUE)
			{
				CloseHandle(m_file_handle);
				m_file_handle = INVALID_HANDLE_VALUE;
			}
		}

		~impl()
		{
			close();
		}

		size_type write(const char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);
			DWORD bytes_written = 0;
			if (num_bytes != 0)
			{
				if (FALSE == WriteFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_written
					, 0))
				{
					throw_exception("file::write");
				}
			}
			return bytes_written;
		}

		size_type read(char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);

			DWORD bytes_read = 0;
			if (num_bytes != 0)
			{
				if (FALSE == ReadFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_read
					, 0))
				{
					throw_exception("file::read");
				}
			}
			return bytes_read;
		}

		void seek(size_type pos, seek_mode from_where)
		{
			assert(pos >= 0 || from_where != seek_begin);
			assert(pos <= 0 || from_where != seek_end);
			LARGE_INTEGER offs;
			offs.QuadPart = pos;
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, from_where))
			{
				throw_exception("file::seek");
			}
		}

		size_type tell()
		{
			LARGE_INTEGER offs;
			offs.QuadPart = 0;

			// is there any other way to get offset?
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, FILE_CURRENT))
			{
				throw_exception("file::tell");
			}

			size_type pos=offs.QuadPart;
			assert(pos>=0);
			return pos;
		}
/*
		size_type size()
		{
			LARGE_INTEGER s;
			if (FALSE == GetFileSizeEx(m_file_handle, &s))
			{
				throw_exception("file::size");
			}

			size_type size = s.QuadPart;
			assert(size >= 0);
			return size;
		}
*/
	private:

		HANDLE m_file_handle;

	};
}

namespace libtorrent
{

	const file::seek_mode file::begin(file::impl::seek_begin);
	const file::seek_mode file::end(file::impl::seek_end);

	const file::open_mode file::in(file::impl::read_flag);
	const file::open_mode file::out(file::impl::write_flag);

	file::file()
		: m_impl(new libtorrent::file::impl())
	{
	}
	file::file(boost::filesystem::path const& p, open_mode m)
		: m_impl(new libtorrent::file::impl())
	{
		open(p,m);
	}

	file::~file()
	{
	}

	void file::open(boost::filesystem::path const& p, open_mode m)
	{
		assert(p.is_complete());
		m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buffer, size_type num_bytes)
	{
		return m_impl->write(buffer, num_bytes);
	}

	size_type file::read(char* buffer, size_type num_bytes)
	{
		return m_impl->read(buffer, num_bytes);
	}

	void file::seek(size_type pos, seek_mode m)
	{
		m_impl->seek(pos,impl::seek_mode(m.m_val));
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}
}
<commit_msg>file_win.cpp fixes<commit_after>/*

Copyright (c) 2003, Magnus Jonsson & Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.

*/

#include "libtorrent/file.hpp"
#include "libtorrent/utf8.hpp"

#include <sstream>
#include <windows.h>

namespace
{
	// must be used to not leak memory in case something would throw
	class auto_localfree
	{
	public:
		auto_localfree(HLOCAL memory)
			: m_memory(memory)
		{
		}
		~auto_localfree()
		{
			if (m_memory)
				LocalFree(m_memory);
		}
	private:
		HLOCAL m_memory;
	};

	std::string utf8_native(std::string const& s)
	{
		std::wstring ws;
		libtorrent::utf8_wchar(s, ws);
		std::size_t size = wcstombs(0, ws.c_str(), 0);
		if (size == std::size_t(-1)) return s;
		std::string ret(size + 1);
		size = wcstombs(&ret[0], ws.c_str(), size + 1);
		ret.resize(size - 1);
		return ret;
	}

	void throw_exception(const char* thrower)
	{
		DWORD err = GetLastError();

#ifdef UNICODE
		wchar_t *wbuffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
		auto_localfree auto_free(wbuffer);
		std::string tmp_utf8;
		libtorrent::wchar_utf8(wbuffer, tmp_utf8);
		char const* buffer = tmp_utf8.c_str();
#else
		char* buffer = 0;
		FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM
			|FORMAT_MESSAGE_ALLOCATE_BUFFER
			, 0, err, 0, (LPSTR)&buffer, 0, 0);
		auto_localfree auto_free(buffer);
#endif

		std::stringstream s;
		s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");

		throw libtorrent::file_error(s.str());
	}
}

namespace libtorrent
{

	struct file::impl : boost::noncopyable
	{
		enum open_flags
		{
			read_flag = 1,
			write_flag = 2
		};

		enum seek_mode
		{
			seek_begin = FILE_BEGIN,
			seek_from_here = FILE_CURRENT,
			seek_end = FILE_END
		};

		impl()
		{
			m_file_handle = INVALID_HANDLE_VALUE;
		}

		void open(const char *file_name, open_flags flags)
		{
			assert(file_name);
			assert(flags & (read_flag | write_flag));

			DWORD access_mask = 0;
			if (flags & read_flag)
				access_mask |= GENERIC_READ;
			if (flags & write_flag)
				access_mask |= GENERIC_WRITE;

			assert(access_mask & (GENERIC_READ | GENERIC_WRITE));

		#ifdef UNICODE
			std::wstring wfile_name(utf8_wchar(file_name));
			HANDLE new_handle = CreateFile(
				(LPCWSTR)wfile_name.c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#else
			HANDLE new_handle = CreateFile(
				utf8_native(file_name).c_str()
				, access_mask
				, FILE_SHARE_READ
				, 0
				, (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING
				, FILE_ATTRIBUTE_NORMAL
				, 0);
		#endif

			if (new_handle == INVALID_HANDLE_VALUE)
			{
				std::stringstream s;
				throw_exception(file_name);
			}
			// will only close old file if the open succeeded
			close();
			m_file_handle = new_handle;
		}

		void close()
		{
			if (m_file_handle != INVALID_HANDLE_VALUE)
			{
				CloseHandle(m_file_handle);
				m_file_handle = INVALID_HANDLE_VALUE;
			}
		}

		~impl()
		{
			close();
		}

		size_type write(const char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);
			DWORD bytes_written = 0;
			if (num_bytes != 0)
			{
				if (FALSE == WriteFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_written
					, 0))
				{
					throw_exception("file::write");
				}
			}
			return bytes_written;
		}

		size_type read(char* buffer, size_type num_bytes)
		{
			assert(buffer);
			assert(num_bytes > 0);
			assert((DWORD)num_bytes == num_bytes);

			DWORD bytes_read = 0;
			if (num_bytes != 0)
			{
				if (FALSE == ReadFile(
					m_file_handle
					, buffer
					, (DWORD)num_bytes
					, &bytes_read
					, 0))
				{
					throw_exception("file::read");
				}
			}
			return bytes_read;
		}

		void seek(size_type pos, seek_mode from_where)
		{
			assert(pos >= 0 || from_where != seek_begin);
			assert(pos <= 0 || from_where != seek_end);
			LARGE_INTEGER offs;
			offs.QuadPart = pos;
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, from_where))
			{
				throw_exception("file::seek");
			}
		}

		size_type tell()
		{
			LARGE_INTEGER offs;
			offs.QuadPart = 0;

			// is there any other way to get offset?
			if (FALSE == SetFilePointerEx(
				m_file_handle
				, offs
				, &offs
				, FILE_CURRENT))
			{
				throw_exception("file::tell");
			}

			size_type pos=offs.QuadPart;
			assert(pos>=0);
			return pos;
		}
/*
		size_type size()
		{
			LARGE_INTEGER s;
			if (FALSE == GetFileSizeEx(m_file_handle, &s))
			{
				throw_exception("file::size");
			}

			size_type size = s.QuadPart;
			assert(size >= 0);
			return size;
		}
*/
	private:

		HANDLE m_file_handle;

	};
}

namespace libtorrent
{

	const file::seek_mode file::begin(file::impl::seek_begin);
	const file::seek_mode file::end(file::impl::seek_end);

	const file::open_mode file::in(file::impl::read_flag);
	const file::open_mode file::out(file::impl::write_flag);

	file::file()
		: m_impl(new libtorrent::file::impl())
	{
	}
	file::file(boost::filesystem::path const& p, open_mode m)
		: m_impl(new libtorrent::file::impl())
	{
		open(p,m);
	}

	file::~file()
	{
	}

	void file::open(boost::filesystem::path const& p, open_mode m)
	{
		assert(p.is_complete());
		m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buffer, size_type num_bytes)
	{
		return m_impl->write(buffer, num_bytes);
	}

	size_type file::read(char* buffer, size_type num_bytes)
	{
		return m_impl->read(buffer, num_bytes);
	}

	void file::seek(size_type pos, seek_mode m)
	{
		m_impl->seek(pos,impl::seek_mode(m.m_val));
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}
}
<|endoftext|>


<commit_before>//  Copyright (c) 2012 Fredrik Mellbin
//
//  This file is part of VapourSynth.
//
//  VapourSynth is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
//
//  VapourSynth 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with VapourSynth.  If not, see <http://www.gnu.org/licenses/>.

#include "vscore.h"

VSThread::VSThread(VSThreadPool *owner) : owner(owner), stop(false) {

}

void VSThread::stopThread() {
    stop = true;
}

void VSThread::run() {
    owner->lock.lock();
    owner->activeThreads++;

    while (true) {
        bool ranTask = false;

        for (int i = 0; i < owner->tasks.count(); i++) {
            PFrameContext rCtx = owner->tasks[i];

            if (rCtx->frameDone && rCtx->returnedFrame) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->returnedFrame);
                ranTask = true;
            } else {

                PFrameContext pCtx = rCtx;

                if (rCtx->returnedFrame || rCtx->hasError())
                    rCtx = rCtx->upstreamContext;

                Q_ASSERT(rCtx);
                Q_ASSERT(pCtx);

                // if an error has already been flagged upstream simply drop this task so a filter won't get multiple arError calls for the same frame
                if (rCtx->hasError()) {
                    owner->tasks.removeAt(i--);
                    continue;
                }

                bool isSingleInstance = rCtx->clip->filterMode == fmSerial || rCtx->clip->filterMode == fmParallelRequests;

                // this check is common for both filter modes, it makes sure that multiple calls won't be made in parallel to a single filter to produce the same frame
                // special casing so serial unordered doesn't need yet another list
                if (owner->runningTasks.contains(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n)))
                    continue;

                if (isSingleInstance) {
                    // this is the complicated case, a new frame may not be started until all calls are completed for the current one
                    if (owner->framesInProgress.contains(rCtx->clip) && owner->framesInProgress[rCtx->clip] != rCtx->n)
                        continue;
                }

                // mark task as active
                owner->tasks.removeAt(i--);
                owner->runningTasks.insert(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n), rCtx);

                if ((rCtx->clip->filterMode == fmParallelRequests && pCtx->returnedFrame) || (rCtx->clip->filterMode == fmSerial))
                    owner->framesInProgress.insert(rCtx->clip, rCtx->n);

                owner->lock.unlock();
                // run task

                ActivationReason ar = arInitial;

                if (pCtx->hasError()) {
                    ar = arError;
                    rCtx->setError(pCtx->getErrorMessage());
                } else if (pCtx != rCtx && pCtx->returnedFrame) {
                    if (rCtx->numFrameRequests.deref())
                        ar = arFrameReady;
                    else
                        ar = arAllFramesReady;

                    Q_ASSERT(rCtx->numFrameRequests >= 0);
                    rCtx->availableFrames.insert(FrameKey(pCtx->clip, pCtx->n), pCtx->returnedFrame);
                    rCtx->lastCompletedN = pCtx->n;
                    rCtx->lastCompletedNode = pCtx->node;
                }

                PVideoFrame f = rCtx->clip->getFrameInternal(rCtx->n, ar, rCtx);
                ranTask = true;
                owner->lock.lock();
                Q_ASSERT(!rCtx->hasError());

                if (f && rCtx->numFrameRequests > 0)
                    qFatal("Frame returned but there are still pending frame requests, filter: " + rCtx->clip->name);

                /*
                PFrameContext a = pCtx;
                char b[100];
                if (a->returnedFrame)
                	a = a->upstreamContext;
                if (rCtx->clip->name == "FFVideoSource") {
                	sprintf(b, "src:\t%d\n", a->n);
                	OutputDebugStringA(b);
                } else if (f) {
                	sprintf(b, "%s:\t%d\t%d\t%d\n", rCtx->clip->name.constData(), ((int)a->clip) & 0xff, a->n, (int)((bool)f));
                	OutputDebugStringA(b);
                }
                */

                owner->runningTasks.remove(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n));

                if (f || ar == arError) {
                    // free all input frames quickly since the frame processing is done
                    rCtx->availableFrames.clear();

                    if (isSingleInstance) {
                        if (owner->framesInProgress[rCtx->clip] != rCtx->n && !rCtx->hasError())
                            qWarning("Releasing unobtained frame lock");

                        owner->framesInProgress.remove(rCtx->clip);
                    }

                    owner->allContexts.remove(FrameKey(rCtx->clip, rCtx->n));
                }

                if (rCtx->hasError()) {
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n) {
                            rCtx->notificationChain.clear();
                            n->setError(rCtx->getErrorMessage());
                        }

                        if (rCtx->upstreamContext) {
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone) {
                            owner->lock.unlock();
                            QMutexLocker callbackLock(&owner->callbackLock);
                            rCtx->frameDone(rCtx->userData, NULL, rCtx->n, rCtx->node, rCtx->getErrorMessage().constData());
                            callbackLock.unlock();
                            owner->lock.lock();
                        }
                    } while (rCtx = n);
                } else if (f) {
                    Q_ASSERT(rCtx->numFrameRequests == 0);
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n)
                            rCtx->notificationChain.clear();

                        if (rCtx->upstreamContext) {
                            rCtx->returnedFrame = f;
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone)
                            owner->returnFrame(rCtx, f);
                    } while (rCtx = n);
                } else if (rCtx->numFrameRequests > 0 || rCtx->n < 0) {
                    // already scheduled or in the case of negative n it is simply a cache notify message
                } else {
                    qFatal("No frame returned at the end of processing by " + rCtx->clip->name);
                }

                break;
            }
        }

        if (!ranTask && !stop) {
            owner->activeThreads--;
            owner->newWork.wait(&owner->lock);
            owner->activeThreads++;
        } else if (stop) {
            owner->activeThreads--;
            owner->lock.unlock();
            return;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threadCount) : core(core), activeThreads(0) {
    setMaxThreadCount(threadCount);
}

int	VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int	VSThreadPool::threadCount() const {
    return allThreads.count();
}

void VSThreadPool::setMaxThreadCount(int threadCount) {
    QMutexLocker m(&lock);

    while (threadCount > allThreads.count()) {
        VSThread *vst = new VSThread(this);
        allThreads.insert(vst);
        vst->start();
    }

    while (threadCount < allThreads.count()) {
        VSThread *t = *allThreads.begin();
        t->stopThread();
        newWork.wakeAll();
        m.unlock();
        t->wait();
        m.relock();
        allThreads.remove(t);
        delete t;
        newWork.wakeAll();
    }
}

void VSThreadPool::wakeThread() {
    if (activeThreads < threadCount())
        newWork.wakeOne();
}

void VSThreadPool::releaseThread() {
    setMaxThreadCount(allThreads.count() + 1);
}

void VSThreadPool::reserveThread() {
    setMaxThreadCount(allThreads.count() - 1);
}

void VSThreadPool::notifyCaches(CacheActivation reason) {
    for (int i = 0; i < core->caches.count(); i++)
        tasks.append(PFrameContext(new FrameContext(reason, core->caches[i], PFrameContext())));
}

void VSThreadPool::start(const PFrameContext &context) {
    Q_ASSERT(context);
    QMutexLocker m(&lock);
    startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    QMutexLocker m(&callbackLock);
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, NULL);
    m.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
        qFatal("Negative frame request by: " + context->clip->name);

    // check to see if it's time to reevaluate cache sizes
    if (!context->upstreamContext && ticks.fetchAndAddAcquire(1) == 99) {
        ticks = 0;
        notifyCaches(cCacheTick);
    }

    // add it immediately if the task is to return a completed frame
    if (context->returnedFrame) {
        tasks.append(context);
        wakeThread();
        return;
    } else {
        if (context->upstreamContext)
            context->upstreamContext->numFrameRequests.ref();

        ////////////////////////
        // see if the task is a duplicate
        foreach(const PFrameContext & ctx, tasks) {
            if (context->clip == ctx->clip && context->n == ctx->n) {
                if (ctx->returnedFrame) {
                    // special case where the requested frame is encountered "by accident"
                    context->returnedFrame = ctx->returnedFrame;
                    tasks.append(context);
                    wakeThread();
                    return;
                } else {
                    PFrameContext rCtx = ctx;

                    if (rCtx->returnedFrame)
                        rCtx = rCtx->upstreamContext;

                    if (context->clip == rCtx->clip && context->n == rCtx->n) {
                        PFrameContext t = rCtx;

                        while (t && t->notificationChain)
                            t = t->notificationChain;

                        t->notificationChain = context;
                        return;
                    }
                }
            }
        }

        FrameKey p(context->clip, context->n);

        if (runningTasks.contains(p)) {
            PFrameContext ctx = runningTasks[p];
            Q_ASSERT(ctx);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                PFrameContext rCtx = ctx;

                if (rCtx->returnedFrame)
                    rCtx = rCtx->upstreamContext;

                if (context->clip == rCtx->clip && context->n == rCtx->n) {
                    PFrameContext t = rCtx;

                    while (t && t->notificationChain)
                        t = t->notificationChain;

                    t->notificationChain = context;
                    return;
                }
            }
        }

        if (allContexts.contains(p)) {
            PFrameContext ctx = allContexts[p];
            Q_ASSERT(ctx);
            Q_ASSERT(context->clip == ctx->clip && context->n == ctx->n);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                while (ctx->notificationChain)
                    ctx = ctx->notificationChain;

                ctx->notificationChain = context;
                return;
            }
        } else {
            allContexts[p] = context;
        }

        tasks.append(context);
        wakeThread();
        return;
    }

}

void VSThreadPool::waitForDone() {

}

VSThreadPool::~VSThreadPool() {
    setMaxThreadCount(0);
};
<commit_msg>Appease Clang (using assignment as condition).<commit_after>//  Copyright (c) 2012 Fredrik Mellbin
//
//  This file is part of VapourSynth.
//
//  VapourSynth is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
//
//  VapourSynth 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with VapourSynth.  If not, see <http://www.gnu.org/licenses/>.

#include "vscore.h"

VSThread::VSThread(VSThreadPool *owner) : owner(owner), stop(false) {

}

void VSThread::stopThread() {
    stop = true;
}

void VSThread::run() {
    owner->lock.lock();
    owner->activeThreads++;

    while (true) {
        bool ranTask = false;

        for (int i = 0; i < owner->tasks.count(); i++) {
            PFrameContext rCtx = owner->tasks[i];

            if (rCtx->frameDone && rCtx->returnedFrame) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->returnedFrame);
                ranTask = true;
            } else {

                PFrameContext pCtx = rCtx;

                if (rCtx->returnedFrame || rCtx->hasError())
                    rCtx = rCtx->upstreamContext;

                Q_ASSERT(rCtx);
                Q_ASSERT(pCtx);

                // if an error has already been flagged upstream simply drop this task so a filter won't get multiple arError calls for the same frame
                if (rCtx->hasError()) {
                    owner->tasks.removeAt(i--);
                    continue;
                }

                bool isSingleInstance = rCtx->clip->filterMode == fmSerial || rCtx->clip->filterMode == fmParallelRequests;

                // this check is common for both filter modes, it makes sure that multiple calls won't be made in parallel to a single filter to produce the same frame
                // special casing so serial unordered doesn't need yet another list
                if (owner->runningTasks.contains(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n)))
                    continue;

                if (isSingleInstance) {
                    // this is the complicated case, a new frame may not be started until all calls are completed for the current one
                    if (owner->framesInProgress.contains(rCtx->clip) && owner->framesInProgress[rCtx->clip] != rCtx->n)
                        continue;
                }

                // mark task as active
                owner->tasks.removeAt(i--);
                owner->runningTasks.insert(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n), rCtx);

                if ((rCtx->clip->filterMode == fmParallelRequests && pCtx->returnedFrame) || (rCtx->clip->filterMode == fmSerial))
                    owner->framesInProgress.insert(rCtx->clip, rCtx->n);

                owner->lock.unlock();
                // run task

                ActivationReason ar = arInitial;

                if (pCtx->hasError()) {
                    ar = arError;
                    rCtx->setError(pCtx->getErrorMessage());
                } else if (pCtx != rCtx && pCtx->returnedFrame) {
                    if (rCtx->numFrameRequests.deref())
                        ar = arFrameReady;
                    else
                        ar = arAllFramesReady;

                    Q_ASSERT(rCtx->numFrameRequests >= 0);
                    rCtx->availableFrames.insert(FrameKey(pCtx->clip, pCtx->n), pCtx->returnedFrame);
                    rCtx->lastCompletedN = pCtx->n;
                    rCtx->lastCompletedNode = pCtx->node;
                }

                PVideoFrame f = rCtx->clip->getFrameInternal(rCtx->n, ar, rCtx);
                ranTask = true;
                owner->lock.lock();
                Q_ASSERT(!rCtx->hasError());

                if (f && rCtx->numFrameRequests > 0)
                    qFatal("Frame returned but there are still pending frame requests, filter: " + rCtx->clip->name);

                /*
                PFrameContext a = pCtx;
                char b[100];
                if (a->returnedFrame)
                	a = a->upstreamContext;
                if (rCtx->clip->name == "FFVideoSource") {
                	sprintf(b, "src:\t%d\n", a->n);
                	OutputDebugStringA(b);
                } else if (f) {
                	sprintf(b, "%s:\t%d\t%d\t%d\n", rCtx->clip->name.constData(), ((int)a->clip) & 0xff, a->n, (int)((bool)f));
                	OutputDebugStringA(b);
                }
                */

                owner->runningTasks.remove(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n));

                if (f || ar == arError) {
                    // free all input frames quickly since the frame processing is done
                    rCtx->availableFrames.clear();

                    if (isSingleInstance) {
                        if (owner->framesInProgress[rCtx->clip] != rCtx->n && !rCtx->hasError())
                            qWarning("Releasing unobtained frame lock");

                        owner->framesInProgress.remove(rCtx->clip);
                    }

                    owner->allContexts.remove(FrameKey(rCtx->clip, rCtx->n));
                }

                if (rCtx->hasError()) {
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n) {
                            rCtx->notificationChain.clear();
                            n->setError(rCtx->getErrorMessage());
                        }

                        if (rCtx->upstreamContext) {
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone) {
                            owner->lock.unlock();
                            QMutexLocker callbackLock(&owner->callbackLock);
                            rCtx->frameDone(rCtx->userData, NULL, rCtx->n, rCtx->node, rCtx->getErrorMessage().constData());
                            callbackLock.unlock();
                            owner->lock.lock();
                        }
                    } while ((rCtx = n));
                } else if (f) {
                    Q_ASSERT(rCtx->numFrameRequests == 0);
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n)
                            rCtx->notificationChain.clear();

                        if (rCtx->upstreamContext) {
                            rCtx->returnedFrame = f;
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone)
                            owner->returnFrame(rCtx, f);
                    } while ((rCtx = n));
                } else if (rCtx->numFrameRequests > 0 || rCtx->n < 0) {
                    // already scheduled or in the case of negative n it is simply a cache notify message
                } else {
                    qFatal("No frame returned at the end of processing by " + rCtx->clip->name);
                }

                break;
            }
        }

        if (!ranTask && !stop) {
            owner->activeThreads--;
            owner->newWork.wait(&owner->lock);
            owner->activeThreads++;
        } else if (stop) {
            owner->activeThreads--;
            owner->lock.unlock();
            return;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threadCount) : core(core), activeThreads(0) {
    setMaxThreadCount(threadCount);
}

int	VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int	VSThreadPool::threadCount() const {
    return allThreads.count();
}

void VSThreadPool::setMaxThreadCount(int threadCount) {
    QMutexLocker m(&lock);

    while (threadCount > allThreads.count()) {
        VSThread *vst = new VSThread(this);
        allThreads.insert(vst);
        vst->start();
    }

    while (threadCount < allThreads.count()) {
        VSThread *t = *allThreads.begin();
        t->stopThread();
        newWork.wakeAll();
        m.unlock();
        t->wait();
        m.relock();
        allThreads.remove(t);
        delete t;
        newWork.wakeAll();
    }
}

void VSThreadPool::wakeThread() {
    if (activeThreads < threadCount())
        newWork.wakeOne();
}

void VSThreadPool::releaseThread() {
    setMaxThreadCount(allThreads.count() + 1);
}

void VSThreadPool::reserveThread() {
    setMaxThreadCount(allThreads.count() - 1);
}

void VSThreadPool::notifyCaches(CacheActivation reason) {
    for (int i = 0; i < core->caches.count(); i++)
        tasks.append(PFrameContext(new FrameContext(reason, core->caches[i], PFrameContext())));
}

void VSThreadPool::start(const PFrameContext &context) {
    Q_ASSERT(context);
    QMutexLocker m(&lock);
    startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    QMutexLocker m(&callbackLock);
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, NULL);
    m.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
        qFatal("Negative frame request by: " + context->clip->name);

    // check to see if it's time to reevaluate cache sizes
    if (!context->upstreamContext && ticks.fetchAndAddAcquire(1) == 99) {
        ticks = 0;
        notifyCaches(cCacheTick);
    }

    // add it immediately if the task is to return a completed frame
    if (context->returnedFrame) {
        tasks.append(context);
        wakeThread();
        return;
    } else {
        if (context->upstreamContext)
            context->upstreamContext->numFrameRequests.ref();

        ////////////////////////
        // see if the task is a duplicate
        foreach(const PFrameContext & ctx, tasks) {
            if (context->clip == ctx->clip && context->n == ctx->n) {
                if (ctx->returnedFrame) {
                    // special case where the requested frame is encountered "by accident"
                    context->returnedFrame = ctx->returnedFrame;
                    tasks.append(context);
                    wakeThread();
                    return;
                } else {
                    PFrameContext rCtx = ctx;

                    if (rCtx->returnedFrame)
                        rCtx = rCtx->upstreamContext;

                    if (context->clip == rCtx->clip && context->n == rCtx->n) {
                        PFrameContext t = rCtx;

                        while (t && t->notificationChain)
                            t = t->notificationChain;

                        t->notificationChain = context;
                        return;
                    }
                }
            }
        }

        FrameKey p(context->clip, context->n);

        if (runningTasks.contains(p)) {
            PFrameContext ctx = runningTasks[p];
            Q_ASSERT(ctx);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                PFrameContext rCtx = ctx;

                if (rCtx->returnedFrame)
                    rCtx = rCtx->upstreamContext;

                if (context->clip == rCtx->clip && context->n == rCtx->n) {
                    PFrameContext t = rCtx;

                    while (t && t->notificationChain)
                        t = t->notificationChain;

                    t->notificationChain = context;
                    return;
                }
            }
        }

        if (allContexts.contains(p)) {
            PFrameContext ctx = allContexts[p];
            Q_ASSERT(ctx);
            Q_ASSERT(context->clip == ctx->clip && context->n == ctx->n);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                while (ctx->notificationChain)
                    ctx = ctx->notificationChain;

                ctx->notificationChain = context;
                return;
            }
        } else {
            allContexts[p] = context;
        }

        tasks.append(context);
        wakeThread();
        return;
    }

}

void VSThreadPool::waitForDone() {

}

VSThreadPool::~VSThreadPool() {
    setMaxThreadCount(0);
};
<|endoftext|>


<commit_before>#include "fragtool.h"

void FragTool::init() {
    initGL();
    shader.log = &(ScreenLog::Instance());
    fragHasChanged = false;
    initShader();
}

void FragTool::destroy() {
    if(hasSound) {
        channel->stop();
        sound->release();
        system->release();
    }
    glDeleteBuffers(1, &vbo);
}

void FragTool::initShader() {
    float vertices[12];

    quad(vertices);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    string fragSource;

    if(!loadFromPath(fragShaderPath, &fragSource)) {
        std::cerr << "Unable to load " << fragShaderPath << std::endl;
        return;
    }

    shader.build(fragSource, vertexShader);

    posAttrib = glGetAttribLocation(shader.getProgram(), "position");
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(posAttrib);
}

void FragTool::setChildProcess(pid_t pid) {
    childProcess = pid;
}

void FragTool::setParentProcess(pid_t pid) {
    parentProcess = pid;
}

void FragTool::setFragShaderPath(const string& shaderPath) {
    fragShaderPath = shaderPath;
}

void FragTool::loadSoundSource(const string& path) {
    hasSound = true;
    soundPath = path;
    FMOD_RESULT result;

    FMOD::System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL,0);
    system->createSound(soundPath.c_str(), FMOD_HARDWARE, 0, &sound);
    //sound->setMode(FMOD_LOOP_OFF);
    system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
}

void FragTool::render() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shader.use();

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    float spectrum[256];
    float wavedata[256];
    system->getWaveData(wavedata, 256, 0);
    system->getSpectrum(spectrum, 256, 0, FMOD_DSP_FFT_WINDOW_TRIANGLE);

    shader.sendUniform("wave", 256, wavedata);
    shader.sendUniform("spectrum", 256, spectrum);
    shader.sendUniform("resolution", width, height);
    shader.sendUniform("time", glfwGetTime());

    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(posAttrib);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void FragTool::fragmentHasChanged() {
    fragHasChanged = true;
}

void FragTool::initGL() {
    width = 800;
    height = 600;

    if(!glfwInit()) {
        handleError("GLFW init failed", -1);
    }

    window = glfwCreateWindow(width, height, "fragtool", NULL, NULL);

    if(!window) {
        glfwTerminate();
        handleError("GLFW create window failed", -1);
    }

    glfwMakeContextCurrent(window);

    glewExperimental = GL_TRUE;
    GLenum err = glewInit();

    if(err != GLEW_OK) {
        cerr << glewGetErrorString(err) << endl;
        handleError("GlEW init failed", -1);
    }

    glfwSetWindowSizeCallback(window, handleResize);
    glfwSetKeyCallback(window, handleKeypress);
}

void FragTool::renderingThread() {
    glClearColor(56.0/255, 101.0/255, 190.0/255, 1);

    while(!glfwWindowShouldClose(window)) {
        glfwSwapBuffers(window);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if(fragHasChanged) {
            string fragSource;

            if(loadFromPath(fragShaderPath, &fragSource)) {
                shader.detach(GL_FRAGMENT_SHADER | GL_VERTEX_SHADER);
                shader.build(fragSource, vertexShader);
                fragHasChanged = false;
            }
        }

        render();
        ScreenLog::Instance().render(true);

        glfwPollEvents();
    }

    glfwTerminate();
}

void FragTool::handleError(const string& message, int exitStatus) {
    cerr << "ABORT: "<< message << endl;
    exit(exitStatus);
}

void FragTool::watchingThread() {
    char s[1024];
    realpath(fragShaderPath.c_str(), s);

    string absolutePath(s);

    watcher = FileWatcher(absolutePath, &watcherCallback);
    watcher.startWatching();
}
<commit_msg>send sound uniforms<commit_after>#include "fragtool.h"

void FragTool::init() {
    initGL();
    shader.log = &(ScreenLog::Instance());
    fragHasChanged = false;
    initShader();
}

void FragTool::destroy() {
    if(hasSound) {
        channel->stop();
        sound->release();
        system->release();
    }
    glDeleteBuffers(1, &vbo);
}

void FragTool::initShader() {
    float vertices[12];

    quad(vertices);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    string fragSource;

    if(!loadFromPath(fragShaderPath, &fragSource)) {
        std::cerr << "Unable to load " << fragShaderPath << std::endl;
        return;
    }

    shader.build(fragSource, vertexShader);

    posAttrib = glGetAttribLocation(shader.getProgram(), "position");
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(posAttrib);
}

void FragTool::setChildProcess(pid_t pid) {
    childProcess = pid;
}

void FragTool::setParentProcess(pid_t pid) {
    parentProcess = pid;
}

void FragTool::setFragShaderPath(const string& shaderPath) {
    fragShaderPath = shaderPath;
}

void FragTool::loadSoundSource(const string& path) {
    hasSound = true;
    soundPath = path;
    FMOD_RESULT result;

    FMOD::System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL,0);
    system->createSound(soundPath.c_str(), FMOD_HARDWARE, 0, &sound);
    //sound->setMode(FMOD_LOOP_OFF);
    //system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
}

void FragTool::render() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shader.use();

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    if(hasSound) {
        float spectrum[256];
        float wavedata[256];
        system->getWaveData(wavedata, 256, 0);
        system->getSpectrum(spectrum, 256, 0, FMOD_DSP_FFT_WINDOW_TRIANGLE);

        shader.sendUniform("wave", 256, wavedata);
        shader.sendUniform("spectrum", 256, spectrum);
    }

    shader.sendUniform("resolution", width, height);
    shader.sendUniform("time", glfwGetTime());

    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(posAttrib);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void FragTool::fragmentHasChanged() {
    fragHasChanged = true;
}

void FragTool::initGL() {
    width = 800;
    height = 600;

    if(!glfwInit()) {
        handleError("GLFW init failed", -1);
    }

    window = glfwCreateWindow(width, height, "fragtool", NULL, NULL);

    if(!window) {
        glfwTerminate();
        handleError("GLFW create window failed", -1);
    }

    glfwMakeContextCurrent(window);

    glewExperimental = GL_TRUE;
    GLenum err = glewInit();

    if(err != GLEW_OK) {
        cerr << glewGetErrorString(err) << endl;
        handleError("GlEW init failed", -1);
    }

    glfwSetWindowSizeCallback(window, handleResize);
    glfwSetKeyCallback(window, handleKeypress);
}

void FragTool::renderingThread() {
    glClearColor(56.0/255, 101.0/255, 190.0/255, 1);

    while(!glfwWindowShouldClose(window)) {
        glfwSwapBuffers(window);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if(fragHasChanged) {
            string fragSource;

            if(loadFromPath(fragShaderPath, &fragSource)) {
                shader.detach(GL_FRAGMENT_SHADER | GL_VERTEX_SHADER);
                shader.build(fragSource, vertexShader);
                fragHasChanged = false;
            }
        }

        render();
        ScreenLog::Instance().render(true);

        glfwPollEvents();
    }

    glfwTerminate();
}

void FragTool::handleError(const string& message, int exitStatus) {
    cerr << "ABORT: "<< message << endl;
    exit(exitStatus);
}

void FragTool::watchingThread() {
    char s[1024];
    realpath(fragShaderPath.c_str(), s);

    string absolutePath(s);

    watcher = FileWatcher(absolutePath, &watcherCallback);
    watcher.startWatching();
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * playlist_item.cpp : Manage playlist item
 ****************************************************************************
 * Copyright © 2006-2011 the VideoLAN team
 * $Id$
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>

#include "qt4.hpp"
#include "playlist_item.hpp"

/*************************************************************************
 * Playlist item implementation
 *************************************************************************/

void AbstractPLItem::clearChildren()
{
    qDeleteAll( children );
    children.clear();
}

void AbstractPLItem::removeChild( AbstractPLItem *item )
{
    children.removeOne( item );
    delete item;
}

/*
   Playlist item is just a wrapper, an abstraction of the playlist_item
   in order to be managed by PLModel

   PLItem have a parent, and id and a input Id
*/

void PLItem::init( playlist_item_t *_playlist_item, PLItem *parent )
{
    parentItem = parent;          /* Can be NULL, but only for the rootItem */
    i_playlist_id = _playlist_item->i_id;           /* Playlist item specific id */
    p_input = _playlist_item->p_input;
    vlc_gc_incref( p_input );
}

/*
   Constructors
   Call the above function init
   */
PLItem::PLItem( playlist_item_t *p_item, PLItem *parent )
{
    init( p_item, parent );
}

PLItem::PLItem( playlist_item_t * p_item )
{
    init( p_item, NULL );
}

PLItem::~PLItem()
{
    vlc_gc_decref( p_input );
    qDeleteAll( children );
    children.clear();
}

int PLItem::id( int type )
{
    switch( type )
    {
    case INPUTITEM_ID:
        return inputItem()->i_id;
    case PLAYLIST_ID:
        return i_playlist_id;
    default:
    case MLMEDIA_ID:
        assert( NULL );
        return -1;
    }
}

void PLItem::takeChildAt( int index )
{
    AbstractPLItem *child = children[index];
    child->parentItem = NULL;
    children.removeAt( index );
}

/* This function is used to get one's parent's row number in the model */
int PLItem::row()
{
    if( parentItem )
        return parentItem->indexOf( this );
    return 0;
}

bool PLItem::operator< ( AbstractPLItem& other )
{
    AbstractPLItem *item1 = this;
    while( item1->parentItem )
    {
        AbstractPLItem *item2 = &other;
        while( item2->parentItem )
        {
            if( item1 == item2->parentItem ) return true;
            if( item2 == item1->parentItem ) return false;
            if( item1->parentItem == item2->parentItem )
                return item1->parentItem->indexOf( item1 ) <
                       item1->parentItem->indexOf( item2 );
            item2 = item2->parentItem;
        }
        item1 = item1->parentItem;
    }
    return false;
}

QUrl PLItem::getURI() const
{
    QString uri;
    vlc_mutex_lock( &p_input->lock );
    uri = QString( p_input->psz_uri );
    vlc_mutex_unlock( &p_input->lock );
    return QUrl( uri );
}

QString PLItem::getTitle() const
{
    QString title;
    char *fb_name = input_item_GetTitle( p_input );
    if( EMPTY_STR( fb_name ) )
    {
        free( fb_name );
        fb_name = input_item_GetName( p_input );
    }
    title = qfu(fb_name);
    free(fb_name);
    return title;
}
<commit_msg>qt4: fix clang warning (implicit conversion)<commit_after>/*****************************************************************************
 * playlist_item.cpp : Manage playlist item
 ****************************************************************************
 * Copyright © 2006-2011 the VideoLAN team
 * $Id$
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>

#include "qt4.hpp"
#include "playlist_item.hpp"

/*************************************************************************
 * Playlist item implementation
 *************************************************************************/

void AbstractPLItem::clearChildren()
{
    qDeleteAll( children );
    children.clear();
}

void AbstractPLItem::removeChild( AbstractPLItem *item )
{
    children.removeOne( item );
    delete item;
}

/*
   Playlist item is just a wrapper, an abstraction of the playlist_item
   in order to be managed by PLModel

   PLItem have a parent, and id and a input Id
*/

void PLItem::init( playlist_item_t *_playlist_item, PLItem *parent )
{
    parentItem = parent;          /* Can be NULL, but only for the rootItem */
    i_playlist_id = _playlist_item->i_id;           /* Playlist item specific id */
    p_input = _playlist_item->p_input;
    vlc_gc_incref( p_input );
}

/*
   Constructors
   Call the above function init
   */
PLItem::PLItem( playlist_item_t *p_item, PLItem *parent )
{
    init( p_item, parent );
}

PLItem::PLItem( playlist_item_t * p_item )
{
    init( p_item, NULL );
}

PLItem::~PLItem()
{
    vlc_gc_decref( p_input );
    qDeleteAll( children );
    children.clear();
}

int PLItem::id( int type )
{
    switch( type )
    {
    case INPUTITEM_ID:
        return inputItem()->i_id;
    case PLAYLIST_ID:
        return i_playlist_id;
    default:
    case MLMEDIA_ID:
        assert( 0 );
        return -1;
    }
}

void PLItem::takeChildAt( int index )
{
    AbstractPLItem *child = children[index];
    child->parentItem = NULL;
    children.removeAt( index );
}

/* This function is used to get one's parent's row number in the model */
int PLItem::row()
{
    if( parentItem )
        return parentItem->indexOf( this );
    return 0;
}

bool PLItem::operator< ( AbstractPLItem& other )
{
    AbstractPLItem *item1 = this;
    while( item1->parentItem )
    {
        AbstractPLItem *item2 = &other;
        while( item2->parentItem )
        {
            if( item1 == item2->parentItem ) return true;
            if( item2 == item1->parentItem ) return false;
            if( item1->parentItem == item2->parentItem )
                return item1->parentItem->indexOf( item1 ) <
                       item1->parentItem->indexOf( item2 );
            item2 = item2->parentItem;
        }
        item1 = item1->parentItem;
    }
    return false;
}

QUrl PLItem::getURI() const
{
    QString uri;
    vlc_mutex_lock( &p_input->lock );
    uri = QString( p_input->psz_uri );
    vlc_mutex_unlock( &p_input->lock );
    return QUrl( uri );
}

QString PLItem::getTitle() const
{
    QString title;
    char *fb_name = input_item_GetTitle( p_input );
    if( EMPTY_STR( fb_name ) )
    {
        free( fb_name );
        fb_name = input_item_GetName( p_input );
    }
    title = qfu(fb_name);
    free(fb_name);
    return title;
}
<|endoftext|>