<commit_before>#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/gl/Texture.h"
#include "cinder/Log.h"
#include "cinder/CinderAssert.h"
#include "cinder/ImageIo.h"
#include "cinder/Text.h"

#include "Resources.h"

#include <boost/format.hpp>

using namespace ci;
using namespace ci::app;
using namespace std;

void prepareSettings( App::Settings *settings )
{
	CI_LOG_I( "bang" );

	const auto &args = settings->getCommandLineArgs();
	if( ! args.empty() ) {
		CI_LOG_I( "command line args: " );
		for( size_t i = 0; i < args.size(); i++ )
			console() << "\t[" << i << "] " << args[i] << endl;
	}

	settings->setWindowPos( 50, 50 );
	settings->setWindowSize( 900, 500 );
	settings->enableHighDensityDisplay();
//	settings->setResizable( false );
//	settings->setBorderless();
//	settings->setAlwaysOnTop();
//	settings->enableMultiTouch();
//	settings->enableMultiTouch( false );
//	settings->disableFrameRate();
//	settings->setFrameRate( 20 );

//	settings->enableStatusBar( false ); // FIXME: status bar is always visible?
//	settings->setTitle( "Blarg" ); // FIXME: seems broken on mac, but did it ever work?
//	settings->setShouldQuit(); // FIXME: currently broken, since member objects continue to try to init

#if defined( CINDER_MSW )
	settings->enableConsoleWindow();
#endif
}

struct SomeMemberObj {
	gl::TextureRef mTex;

	SomeMemberObj()
	{
		CI_LOG_I( "bang" );

		auto resource = loadResource( RES_IMAGE );
		mTex = gl::Texture::create( loadImage( resource ) );

		CI_CHECK_GL();
	}
};

class AppTestApp : public App {
  public:
	AppTestApp();

	void setup() override;
	void keyDown( KeyEvent event ) override;
	void mouseDown( MouseEvent event ) override;
	void touchesBegan( TouchEvent event ) override;
	void touchesMoved( TouchEvent event ) override;
	void touchesEnded( TouchEvent event ) override;
	void resize() override;
	void update() override;
	void draw() override;

	void drawInfo();

	SomeMemberObj mSomeMemberObj;

	gl::TextureRef mTexStartup, mTexAsset, mTexResource;

};

AppTestApp::AppTestApp()
{
	CI_LOG_I( "bang" );

//	Surface8u surface( 140, 140, false );
//	Surface8u::Iter iter = surface.getIter();
//	while( iter.line() ) {
//		while( iter.pixel() ) {
//			iter.r() = 0;
//			iter.g() = iter.x();
//			iter.b() = iter.y();
//		}
//	}

//	mTexStartup = gl::Texture::create( surface );

	auto asset = loadAsset( "mustache-green.png" );
	mTexStartup = gl::Texture::create( loadImage( asset ) );

	CI_CHECK_GL();
}

void AppTestApp::setup()
{
	CI_LOG_I( "bang" );

	auto asset = loadAsset( "mustache-green.png" );
	mTexAsset = gl::Texture::create( loadImage( asset ) );

	auto resource = loadResource( RES_IMAGE );
	mTexResource = gl::Texture::create( loadImage( resource ) );

	gl::enableAlphaBlending();

	CI_LOG_I( "target fps: " << getFrameRate() );

	CI_CHECK_GL();
	CI_LOG_I( "complete" );
}

void AppTestApp::keyDown( KeyEvent event )
{
	if( event.getChar() == 'f' ) {
		setFullScreen( ! isFullScreen() );
	}
	else if( event.getChar() == 'o' ) {
		auto filePath = getOpenFilePath();
		CI_LOG_I( "result of getOpenFilePath(): " << filePath );
	}
	else if( event.getChar() == 'p' ) {
		auto filePath = getFolderPath();
		CI_LOG_I( "result of getFolderPath(): " << filePath );
	}
	else if( event.getChar() == 's' ) {
		auto filePath = getSaveFilePath();
		CI_LOG_I( "result of getSaveFilePath(): " << filePath );
	}
}

void AppTestApp::mouseDown( MouseEvent event )
{
	CI_LOG_I( "mouse pos: " << event.getPos() );
}

void AppTestApp::touchesBegan( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches:" << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::touchesMoved( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches:" << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::touchesEnded( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches: " << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::resize()
{
	CI_LOG_I( "window pos: " << getWindowPos() << ", size: " << getWindowSize() << ", pixel size: " << toPixels( getWindowSize() ) );
}

void AppTestApp::update()
{
}

void AppTestApp::draw()
{
	gl::clear( Color( 0.2f, 0.1f, 0 ) );

	// draw something animated
	{
		gl::ScopedColor colorScope( ColorA( 0, 0, 1, 0.5f ) );
		gl::ScopedModelMatrix modelScope;
		gl::translate( getWindowCenter() );
		gl::rotate( getElapsedSeconds() * 0.5, vec3( 0, 0, 1 ) );

		const float t = 200;
		gl::drawSolidRect( Rectf( -t, -t, t, t ) );
	}

	// draw test textures

	auto offset = vec2( 0 );

	if( mTexAsset ) {
		gl::draw( mTexAsset, offset );
	}

	if( mTexResource ) {
		offset.x += 256;
		gl::draw( mTexResource, offset );
	}

	if( mTexStartup ) {
		offset.x = 0;
		offset.y += 200;
		gl::draw( mTexStartup, offset );
	}

	if( mSomeMemberObj.mTex ) {
		offset.x += mSomeMemberObj.mTex->getWidth();
		gl::draw( mSomeMemberObj.mTex, offset );
	}

	drawInfo();

	CI_CHECK_GL();
}

void AppTestApp::drawInfo()
{
	TextLayout layout;
	layout.setFont( Font( "Arial", 16 ) );
	layout.setColor( ColorA::gray( 0.9f, 0.75f ) );

	auto fps = boost::format( "%0.2f" ) % getAverageFps();
	layout.addLine( "fps: " + fps.str() );
	layout.addLine( "v-sync enabled: " + string( gl::isVerticalSyncEnabled() ? "true" : "false" ) );

	auto tex = gl::Texture::create( layout.render( true ) );

	vec2 offset( 6, getWindowHeight() - tex->getHeight() - 6 ); // bottom left

	gl::color( Color::white() );
	gl::draw( tex, offset );
}

// no settings fn:
//CINDER_APP( AppTestApp, RendererGl )

// settings fn from top of file:
CINDER_APP( AppTestApp, RendererGl, prepareSettings )

// settings fn by lambda
//CINDER_APP( AppTestApp, RendererGl, []( AppTestApp::Settings *settings ) {
//	CI_LOG_I( "bang" );
//} )
<commit_msg>added user app destructor log for testing<commit_after>#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/gl/Texture.h"
#include "cinder/Log.h"
#include "cinder/CinderAssert.h"
#include "cinder/ImageIo.h"
#include "cinder/Text.h"

#include "Resources.h"

#include <boost/format.hpp>

using namespace ci;
using namespace ci::app;
using namespace std;

void prepareSettings( App::Settings *settings )
{
	CI_LOG_I( "bang" );

	const auto &args = settings->getCommandLineArgs();
	if( ! args.empty() ) {
		CI_LOG_I( "command line args: " );
		for( size_t i = 0; i < args.size(); i++ )
			console() << "\t[" << i << "] " << args[i] << endl;
	}

	settings->setWindowPos( 50, 50 );
	settings->setWindowSize( 900, 500 );
	settings->enableHighDensityDisplay();
//	settings->setResizable( false );
//	settings->setBorderless();
//	settings->setAlwaysOnTop();
//	settings->enableMultiTouch();
//	settings->enableMultiTouch( false );
//	settings->disableFrameRate();
//	settings->setFrameRate( 20 );

//	settings->enableStatusBar( false ); // FIXME: status bar is always visible?
//	settings->setTitle( "Blarg" ); // FIXME: seems broken on mac, but did it ever work?
//	settings->setShouldQuit(); // FIXME: currently broken, since member objects continue to try to init

#if defined( CINDER_MSW )
	settings->enableConsoleWindow();
#endif
}

struct SomeMemberObj {
	gl::TextureRef mTex;

	SomeMemberObj()
	{
		CI_LOG_I( "bang" );

		auto resource = loadResource( RES_IMAGE );
		mTex = gl::Texture::create( loadImage( resource ) );

		CI_CHECK_GL();
	}
};

class AppTestApp : public App {
  public:
	AppTestApp();
	~AppTestApp();

	void setup() override;
	void keyDown( KeyEvent event ) override;
	void mouseDown( MouseEvent event ) override;
	void touchesBegan( TouchEvent event ) override;
	void touchesMoved( TouchEvent event ) override;
	void touchesEnded( TouchEvent event ) override;
	void resize() override;
	void update() override;
	void draw() override;

	void drawInfo();

	SomeMemberObj mSomeMemberObj;

	gl::TextureRef mTexStartup, mTexAsset, mTexResource;

};

AppTestApp::AppTestApp()
{
	CI_LOG_I( "bang" );

//	Surface8u surface( 140, 140, false );
//	Surface8u::Iter iter = surface.getIter();
//	while( iter.line() ) {
//		while( iter.pixel() ) {
//			iter.r() = 0;
//			iter.g() = iter.x();
//			iter.b() = iter.y();
//		}
//	}

//	mTexStartup = gl::Texture::create( surface );

	auto asset = loadAsset( "mustache-green.png" );
	mTexStartup = gl::Texture::create( loadImage( asset ) );

	CI_CHECK_GL();
}

AppTestApp::~AppTestApp()
{
	CI_LOG_I( "bang" );
}

void AppTestApp::setup()
{
	CI_LOG_I( "bang" );

	auto asset = loadAsset( "mustache-green.png" );
	mTexAsset = gl::Texture::create( loadImage( asset ) );

	auto resource = loadResource( RES_IMAGE );
	mTexResource = gl::Texture::create( loadImage( resource ) );

	gl::enableAlphaBlending();

	CI_LOG_I( "target fps: " << getFrameRate() );

	CI_CHECK_GL();
	CI_LOG_I( "complete" );
}

void AppTestApp::keyDown( KeyEvent event )
{
	if( event.getChar() == 'f' ) {
		setFullScreen( ! isFullScreen() );
	}
	else if( event.getChar() == 'o' ) {
		auto filePath = getOpenFilePath();
		CI_LOG_I( "result of getOpenFilePath(): " << filePath );
	}
	else if( event.getChar() == 'p' ) {
		auto filePath = getFolderPath();
		CI_LOG_I( "result of getFolderPath(): " << filePath );
	}
	else if( event.getChar() == 's' ) {
		auto filePath = getSaveFilePath();
		CI_LOG_I( "result of getSaveFilePath(): " << filePath );
	}
}

void AppTestApp::mouseDown( MouseEvent event )
{
	CI_LOG_I( "mouse pos: " << event.getPos() );
}

void AppTestApp::touchesBegan( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches:" << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::touchesMoved( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches:" << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::touchesEnded( TouchEvent event )
{
	const auto &touches = event.getTouches();
	CI_LOG_I( "num touches: " << touches.size() );

	for( size_t i = 0; i < touches.size(); i++ )
		console() << "\t\t[" << i << "] pos: " << touches.at( i ).getPos() << endl;
}

void AppTestApp::resize()
{
	CI_LOG_I( "window pos: " << getWindowPos() << ", size: " << getWindowSize() << ", pixel size: " << toPixels( getWindowSize() ) );
}

void AppTestApp::update()
{
}

void AppTestApp::draw()
{
	gl::clear( Color( 0.2f, 0.1f, 0 ) );

	// draw something animated
	{
		gl::ScopedColor colorScope( ColorA( 0, 0, 1, 0.5f ) );
		gl::ScopedModelMatrix modelScope;
		gl::translate( getWindowCenter() );
		gl::rotate( getElapsedSeconds() * 0.5, vec3( 0, 0, 1 ) );

		const float t = 200;
		gl::drawSolidRect( Rectf( -t, -t, t, t ) );
	}

	// draw test textures

	auto offset = vec2( 0 );

	if( mTexAsset ) {
		gl::draw( mTexAsset, offset );
	}

	if( mTexResource ) {
		offset.x += 256;
		gl::draw( mTexResource, offset );
	}

	if( mTexStartup ) {
		offset.x = 0;
		offset.y += 200;
		gl::draw( mTexStartup, offset );
	}

	if( mSomeMemberObj.mTex ) {
		offset.x += mSomeMemberObj.mTex->getWidth();
		gl::draw( mSomeMemberObj.mTex, offset );
	}

	drawInfo();

	CI_CHECK_GL();
}

void AppTestApp::drawInfo()
{
	TextLayout layout;
	layout.setFont( Font( "Arial", 16 ) );
	layout.setColor( ColorA::gray( 0.9f, 0.75f ) );

	auto fps = boost::format( "%0.2f" ) % getAverageFps();
	layout.addLine( "fps: " + fps.str() );
	layout.addLine( "v-sync enabled: " + string( gl::isVerticalSyncEnabled() ? "true" : "false" ) );

	auto tex = gl::Texture::create( layout.render( true ) );

	vec2 offset( 6, getWindowHeight() - tex->getHeight() - 6 ); // bottom left

	gl::color( Color::white() );
	gl::draw( tex, offset );
}

// no settings fn:
//CINDER_APP( AppTestApp, RendererGl )

// settings fn from top of file:
CINDER_APP( AppTestApp, RendererGl, prepareSettings )

// settings fn by lambda
//CINDER_APP( AppTestApp, RendererGl, []( AppTestApp::Settings *settings ) {
//	CI_LOG_I( "bang" );
//} )
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2021 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/core/timed_out_error.hh>
#include <seastar/core/coroutine.hh>
#include <seastar/core/future-util.hh>

#include "raft/logical_clock.hh"

using namespace seastar;

constexpr raft::logical_clock::duration operator "" _t(unsigned long long ticks) {
    return raft::logical_clock::duration{ticks};
}

// A wrapper around `raft::logical_clock` that allows scheduling events to happen after a certain number of ticks.
class logical_timer {
    struct scheduled_impl {
    private:
        bool _resolved = false;

        virtual void do_resolve() = 0;

    public:
        virtual ~scheduled_impl() { }

        void resolve() {
            if (!_resolved) {
                do_resolve();
                _resolved = true;
            }
        }

        void mark_resolved() { _resolved = true; }
        bool resolved() { return _resolved; }
    };

    struct scheduled {
        raft::logical_clock::time_point _at;
        seastar::shared_ptr<scheduled_impl> _impl;

        void resolve() { _impl->resolve(); }
        bool resolved() { return _impl->resolved(); }
    };

    raft::logical_clock _clock;

    // A min-heap of `scheduled` events sorted by `_at`.
    std::vector<scheduled> _scheduled;

    // Comparator for the `_scheduled` min-heap.
    static bool cmp(const scheduled& a, const scheduled& b) {
        return a._at > b._at;
    }

public:
    template <typename... T>
    class timed_out : public raft::error {
        // lw_shared_ptr to make the exception copyable
        lw_shared_ptr<seastar::future<T...>> _fut;

    public:
        timed_out(seastar::future<T...> f) : error("timed out"), _fut(make_lw_shared(std::move(f))) {}
        timed_out(const timed_out&) = default;
        timed_out(timed_out&&) = default;

        seastar::future<T...>& get_future() {
            return *_fut;
        }
    };

    logical_timer() = default;

    logical_timer(const logical_timer&) = delete;
    logical_timer(logical_timer&&) = default;

    // Returns the current logical time (number of `tick()`s since initialization).
    raft::logical_clock::time_point now() {
        return _clock.now();
    }

    // Tick the internal clock.
    // Resolve all events whose scheduled times arrive after that tick.
    void tick() {
        _clock.advance();
        while (!_scheduled.empty() && _scheduled.front()._at <= _clock.now()) {
            _scheduled.front().resolve();
            std::pop_heap(_scheduled.begin(), _scheduled.end(), cmp);
            _scheduled.pop_back();
        }
    }

    // Given a future `f` and a logical time point `tp`, returns a future that resolves
    // when either `f` resolves or the time point `tp` arrives (according to the number of `tick()` calls),
    // whichever comes first.
    //
    // If `tp` comes first, the returned future is resolved with the `timed_out` exception.
    // The exception contains a future equivalent to the original future (i.e. when the original future
    // resolves, the future inside the exception will contain the original future's result).
    //
    // Note: it is highly recommended to pass in futures that always resolve eventually
    // since we attach continuations to these futures, allocating memory.
    //
    // Note: there is a possibility that the internal heap will grow unbounded if we call `with_timeout`
    // more often than we `tick`, so don't do that. It is recommended to call at least one
    // `tick` per one `with_timeout` call (on average in the long run).
    template <typename... T>
    future<T...> with_timeout(raft::logical_clock::time_point tp, future<T...> f) {
        if (f.available()) {
            return f;
        }

        struct sched : public scheduled_impl {
            // The original future (the `f` argument), when it resolves, will set value on `_p`.
            // Before timeout, `_p` is connected to the future returned to the user (`res` below).
            // After timeout, `_p` is a new promise connected to the future returned inside the timed_out exception.
            // Therefore:
            // 1. if `f` resolves before timeout, it will resolve `res`,
            // 2. otherwise it will resolve the future we return inside `timed_out` which is returned through `res`.
            promise<T...> _p;

            virtual ~sched() override { }
            virtual void do_resolve() override {
                promise<T...> new_p;
                _p.set_exception(timed_out<T...>{new_p.get_future()});
                std::swap(new_p, _p);
            }
        };

        auto s = make_shared<sched>();
        auto res = s->_p.get_future();

        _scheduled.push_back(scheduled{
            ._at = tp,
            ._impl = s
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        (void)f.then_wrapped([s = std::move(s)] (future<T...> f) mutable {
            // If we're before timeout, `s->_p` is connected to `res` so we're returning the result of `f` to our caller.
            // Otherwise `s->_p` is connected to a future which was previously returned to our caller inside `timed_out`.
            f.forward_to(std::move(s->_p));
            s->mark_resolved();
            // tick() will (eventually) clear the `_scheduled` entry
            // (unless we're already past timeout, in which case the entry has already been cleared).
        });

        return res;
    }

    // Returns a future that resolves after a number of `tick()`s represented by `d`.
    // Example usage: `sleep(20_t)` resolves after 20 `tick()`s.
    // Note: analogous remark applies as for `with_timeout`, i.e. make sure to call at least one `tick`
    // per one `sleep` call on average.
    future<> sleep(raft::logical_clock::duration d) {
        if (d == raft::logical_clock::duration{0}) {
            return make_ready_future<>();
        }

        struct sched : public scheduled_impl {
            promise<> _p;
            virtual ~sched() override {}
            virtual void do_resolve() override { _p.set_value(); }
        };

        auto s = make_shared<sched>();
        auto f = s->_p.get_future();
        _scheduled.push_back(scheduled{
            ._at = now() + d,
            ._impl = std::move(s)
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        return f;
    }

    // Schedule `f` to be called at logical time point `tp` (according to this timer's clock).
    template <typename F>
    void schedule(raft::logical_clock::time_point tp, F f) {
        if (tp <= now()) {
            f();
            return;
        }

        struct sched : public scheduled_impl {
            sched(F f) : _f(std::move(f)) {}
            virtual ~sched() override {}
            virtual void do_resolve() override { _f(); }

            F _f;
        };

        _scheduled.push_back(scheduled {
            ._at = tp,
            ._impl = make_shared<sched>(std::move(f))
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);
    }
};

<commit_msg>test: raft: logical_timer: handle immediate timeout<commit_after>/*
 * Copyright (C) 2021 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/core/timed_out_error.hh>
#include <seastar/core/coroutine.hh>
#include <seastar/core/future-util.hh>

#include "raft/logical_clock.hh"

using namespace seastar;

constexpr raft::logical_clock::duration operator "" _t(unsigned long long ticks) {
    return raft::logical_clock::duration{ticks};
}

// A wrapper around `raft::logical_clock` that allows scheduling events to happen after a certain number of ticks.
class logical_timer {
    struct scheduled_impl {
    private:
        bool _resolved = false;

        virtual void do_resolve() = 0;

    public:
        virtual ~scheduled_impl() { }

        void resolve() {
            if (!_resolved) {
                do_resolve();
                _resolved = true;
            }
        }

        void mark_resolved() { _resolved = true; }
        bool resolved() { return _resolved; }
    };

    struct scheduled {
        raft::logical_clock::time_point _at;
        seastar::shared_ptr<scheduled_impl> _impl;

        void resolve() { _impl->resolve(); }
        bool resolved() { return _impl->resolved(); }
    };

    raft::logical_clock _clock;

    // A min-heap of `scheduled` events sorted by `_at`.
    std::vector<scheduled> _scheduled;

    // Comparator for the `_scheduled` min-heap.
    static bool cmp(const scheduled& a, const scheduled& b) {
        return a._at > b._at;
    }

public:
    template <typename... T>
    class timed_out : public raft::error {
        // lw_shared_ptr to make the exception copyable
        lw_shared_ptr<seastar::future<T...>> _fut;

    public:
        timed_out(seastar::future<T...> f) : error("timed out"), _fut(make_lw_shared(std::move(f))) {}
        timed_out(const timed_out&) = default;
        timed_out(timed_out&&) = default;

        seastar::future<T...>& get_future() {
            return *_fut;
        }
    };

    logical_timer() = default;

    logical_timer(const logical_timer&) = delete;
    logical_timer(logical_timer&&) = default;

    // Returns the current logical time (number of `tick()`s since initialization).
    raft::logical_clock::time_point now() {
        return _clock.now();
    }

    // Tick the internal clock.
    // Resolve all events whose scheduled times arrive after that tick.
    void tick() {
        _clock.advance();
        while (!_scheduled.empty() && _scheduled.front()._at <= _clock.now()) {
            _scheduled.front().resolve();
            std::pop_heap(_scheduled.begin(), _scheduled.end(), cmp);
            _scheduled.pop_back();
        }
    }

    // Given a future `f` and a logical time point `tp`, returns a future that resolves
    // when either `f` resolves or the time point `tp` arrives (according to the number of `tick()` calls),
    // whichever comes first.
    //
    // If `tp` comes first, the returned future is resolved with the `timed_out` exception.
    // The exception contains a future equivalent to the original future (i.e. when the original future
    // resolves, the future inside the exception will contain the original future's result).
    //
    // Note: it is highly recommended to pass in futures that always resolve eventually
    // since we attach continuations to these futures, allocating memory.
    //
    // Note: there is a possibility that the internal heap will grow unbounded if we call `with_timeout`
    // more often than we `tick`, so don't do that. It is recommended to call at least one
    // `tick` per one `with_timeout` call (on average in the long run).
    template <typename... T>
    future<T...> with_timeout(raft::logical_clock::time_point tp, future<T...> f) {
        if (f.available()) {
            return f;
        }

        if (tp <= now()) {
            return make_exception_future<T...>(timed_out<T...>{std::move(f)});
        }

        struct sched : public scheduled_impl {
            // The original future (the `f` argument), when it resolves, will set value on `_p`.
            // Before timeout, `_p` is connected to the future returned to the user (`res` below).
            // After timeout, `_p` is a new promise connected to the future returned inside the timed_out exception.
            // Therefore:
            // 1. if `f` resolves before timeout, it will resolve `res`,
            // 2. otherwise it will resolve the future we return inside `timed_out` which is returned through `res`.
            promise<T...> _p;

            virtual ~sched() override { }
            virtual void do_resolve() override {
                promise<T...> new_p;
                _p.set_exception(timed_out<T...>{new_p.get_future()});
                std::swap(new_p, _p);
            }
        };

        auto s = make_shared<sched>();
        auto res = s->_p.get_future();

        _scheduled.push_back(scheduled{
            ._at = tp,
            ._impl = s
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        (void)f.then_wrapped([s = std::move(s)] (future<T...> f) mutable {
            // If we're before timeout, `s->_p` is connected to `res` so we're returning the result of `f` to our caller.
            // Otherwise `s->_p` is connected to a future which was previously returned to our caller inside `timed_out`.
            f.forward_to(std::move(s->_p));
            s->mark_resolved();
            // tick() will (eventually) clear the `_scheduled` entry
            // (unless we're already past timeout, in which case the entry has already been cleared).
        });

        return res;
    }

    // Returns a future that resolves after a number of `tick()`s represented by `d`.
    // Example usage: `sleep(20_t)` resolves after 20 `tick()`s.
    // Note: analogous remark applies as for `with_timeout`, i.e. make sure to call at least one `tick`
    // per one `sleep` call on average.
    future<> sleep(raft::logical_clock::duration d) {
        if (d == raft::logical_clock::duration{0}) {
            return make_ready_future<>();
        }

        struct sched : public scheduled_impl {
            promise<> _p;
            virtual ~sched() override {}
            virtual void do_resolve() override { _p.set_value(); }
        };

        auto s = make_shared<sched>();
        auto f = s->_p.get_future();
        _scheduled.push_back(scheduled{
            ._at = now() + d,
            ._impl = std::move(s)
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        return f;
    }

    // Schedule `f` to be called at logical time point `tp` (according to this timer's clock).
    template <typename F>
    void schedule(raft::logical_clock::time_point tp, F f) {
        if (tp <= now()) {
            f();
            return;
        }

        struct sched : public scheduled_impl {
            sched(F f) : _f(std::move(f)) {}
            virtual ~sched() override {}
            virtual void do_resolve() override { _f(); }

            F _f;
        };

        _scheduled.push_back(scheduled {
            ._at = tp,
            ._impl = make_shared<sched>(std::move(f))
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);
    }
};

<|endoftext|>


<commit_before>#include "ProgramReader.h"
const std::string ProgramReader::MAGIC = "THING";
void ProgramReader::read_header()
{
	if (!file.is_open()) {
		throw std::exception("Cannot open file");
	}

	auto magic = read(MAGIC.size());

	if (magic != MAGIC) {
		throw std::exception("Invalid file format");
	}

	auto version = read<uint16_t>();
	program_size = read<uint32_t>();
	data_size = read<uint32_t>();
	index = 0; // reset the index, since the program_size in the header does not include the header itself

	std::cerr << "thinglang bytecode version: " << version << ", total size: " << program_size << ", data size: " << data_size << std::endl;
}

std::vector<PThingInstance> ProgramReader::read_data() {
	std::cerr << "Reading data section..." << std::endl;

	std::vector<PThingInstance> static_data;

	while (in_data()) {
		static_data.push_back(read_data_block());
	}

	return static_data;

}

PThingInstance ProgramReader::read_data_block() {
	auto type = read<int32_t>();

	switch (type) {
	case -1: {
		auto size = read_size();
		auto data = read(size);
		std::cerr << "\tReading text (" << size << " bytes): " << data << std::endl;
		return PThingInstance(new TextInstance(data));
	}
	case -2: {
		auto data = read<int32_t>();
		std::cerr << "\tReading int: " << data << std::endl;
		return PThingInstance(new NumberInstance(data));
	}

	default:
		throw std::exception("Unknown static data type");

	}
}
MethodDefinition ProgramReader::read_method(size_t size) {
	uint32_t frame_size = read_size();
	uint32_t arguments = read_size();
	uint32_t last_index = index + size;

	std::cerr << "\tEncountered method (size=" << size << ", frame size=" << frame_size << ", args=" << arguments << ")" << std::endl;
	std::vector<Symbol> symbols;

	while (index < last_index) {
		symbols.push_back(read_symbol());
	}

	return MethodDefinition(frame_size, arguments, symbols);
}

<commit_msg>Class level iterative bytecode reader<commit_after>#include "ProgramReader.h"
const std::string ProgramReader::MAGIC = "THING";
void ProgramReader::read_header()
{
	if (!file.is_open()) {
		throw std::exception("Cannot open file");
	}

	auto magic = read(MAGIC.size());

	if (magic != MAGIC) {
		throw std::exception("Invalid file format");
	}

	auto version = read<uint16_t>();
	program_size = read<uint32_t>();
	data_size = read<uint32_t>();
	index = 0; // reset the index, since the program_size in the header does not include the header itself

	std::cerr << "thinglang bytecode version: " << version << ", total size: " << program_size << ", data size: " << data_size << std::endl;
}

std::vector<PThingInstance> ProgramReader::read_data() {
	std::cerr << "Reading data section..." << std::endl;

	std::vector<PThingInstance> static_data;

	while (in_data()) {
		static_data.push_back(read_data_block());
	}

	return static_data;

}

PThingInstance ProgramReader::read_data_block() {
	auto type = read<int32_t>();

	switch (type) {
	case -1: {
		auto size = read_size();
		auto data = read(size);
		std::cerr << "\tReading text (" << size << " bytes): " << data << std::endl;
		return PThingInstance(new TextInstance(data));
	}
	case -2: {
		auto data = read<int32_t>();
		std::cerr << "\tReading int: " << data << std::endl;
		return PThingInstance(new NumberInstance(data));
	}

	default:
		throw std::exception("Unknown static data type");

	}
}

std::vector<TypeInfo> ProgramReader::read_code() {

	std::cerr << "Reading program..." << std::endl;
	std::vector<TypeInfo> user_types;

	while (in_program()) {
		auto class_definition_size = read_size();
		std::cerr << "\tEncountered class (id=" << (user_types.size() - 1) << ") of size " << class_definition_size << std::endl;
		user_types.push_back(read_class(class_definition_size));
	}

	if (index != program_size) {
		throw std::exception(std::string("Index mismatch " + std::to_string(index) + ", " + std::to_string(program_size)).c_str());
	}
	else {
		std::cerr << "Program processed successfully" << std::endl << std::endl;
	}

	return user_types;
}

TypeInfo ProgramReader::read_class(uint32_t size) {
	uint32_t last_index = index + size;
	std::vector<MethodDefinition> methods;

	while (index < last_index) {
		auto method_size = read_size();
		methods.push_back(read_method(method_size));
	}
	return TypeInfo("Unknown class", "", methods);

}

MethodDefinition ProgramReader::read_method(size_t size) {
	uint32_t frame_size = read_size();
	uint32_t arguments = read_size();
	uint32_t last_index = index + size;

	std::cerr << "\tEncountered method (size=" << size << ", frame size=" << frame_size << ", args=" << arguments << ")" << std::endl;
	std::vector<Symbol> symbols;

	while (index < last_index) {
		symbols.push_back(read_symbol());
	}

	return MethodDefinition(frame_size, arguments, symbols);
}

<|endoftext|>


<commit_before>#include "ToolOptionsPage.h"
#include "ToolRunner.h"
#include "CheckTypes.h"
#include "ClangToolsConstants.h"

#include <coreplugin/icore.h>
#include <coreplugin/variablechooser.h>

#include <QWidget>
#include <QCheckBox>
#include <QLineEdit>
#include <QLabel>
#include <QBoxLayout>
#include <QPushButton>
#include <QFileDialog>

namespace QtcUtilities {
namespace Internal {
namespace ClangTools {

const QString SETTINGS_GROUP = QLatin1String ("ClangTools");
const QString SETTINGS_NAME = QLatin1String ("name");
const QString SETTINGS_BINARY = QLatin1String ("binary");
const QString SETTINGS_ARGUMENTS = QLatin1String ("arguments");
const QString SETTINGS_EXTENSIONS = QLatin1String ("extensions");
const QString SETTINGS_CHECKTYPES = QLatin1String ("checkTypes");

const QString NAME_TIDY = QLatin1String ("Tidy");
const QString NAME_MODULARIZE = QLatin1String ("Modularize");
const QString NAME_CHECK = QLatin1String ("Check");

class ToolOptionsWidget : public QWidget
{
  Q_OBJECT

  public:
    explicit ToolOptionsWidget (Core::VariableChooser *variableChooser);
    void set (const ToolRunnerSettings &settings);
    void get (ToolRunnerSettings &settings) const;

  private slots:
    void browseBinary ();

  private:
    QLabel *title_;
    QLineEdit *binary_;
    QPushButton *browseBinary_;
    QLineEdit *arguments_;
    QLineEdit *extensions_;
    QCheckBox *checkOnFileAdd_;
    QCheckBox *checkOnFileChange_;
    QCheckBox *checkOnProjectBuild_;
    QCheckBox *checkOnProjectSwitch_;

    QMap<QCheckBox *, int> typeMap_;
};

ToolOptionsWidget::ToolOptionsWidget (Core::VariableChooser *variableChooser) :
  title_ (new QLabel),
  binary_ (new QLineEdit), browseBinary_ (new QPushButton (tr ("Browse..."))),
  arguments_ (new QLineEdit), extensions_ (new QLineEdit),
  checkOnFileAdd_ (new QCheckBox (tr ("Check on file add"))),
  checkOnFileChange_ (new QCheckBox (tr ("Check on file change"))),
  checkOnProjectBuild_ (new QCheckBox (tr ("Check on project build"))),
  checkOnProjectSwitch_ (new QCheckBox (tr ("Check on project switch")))
{
  auto layout = new QGridLayout;

  auto row = 0;
  layout->addWidget (title_, row, 0, 1, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Binary:")), row, 0);
  layout->addWidget (binary_, row, 1);
  layout->addWidget (browseBinary_, row, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Arguments:")), row, 0);
  layout->addWidget (arguments_, row, 1, 1, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Extensions:")), row, 0);
  layout->addWidget (extensions_, row, 1, 1, 2);

  ++row;
  layout->addWidget (checkOnFileAdd_, row, 0);
  layout->addWidget (checkOnFileChange_, row, 1, 1, 2);

  ++row;
  layout->addWidget (checkOnProjectBuild_, row, 0);
  layout->addWidget (checkOnProjectSwitch_, row, 1, 1, 2);

  setLayout (layout);

  extensions_->setToolTip (tr ("Comma separated. May be empty."));

  variableChooser->addSupportedWidget (arguments_);
  connect (browseBinary_, &QPushButton::clicked, this, &ToolOptionsWidget::browseBinary);

  typeMap_.insert (checkOnFileAdd_, CheckOnFileAdd);
  typeMap_.insert (checkOnFileChange_, CheckOnFileChange);
  typeMap_.insert (checkOnProjectBuild_, CheckOnProjectBuild);
  typeMap_.insert (checkOnProjectSwitch_, CheckOnProjectSwitch);
}

void ToolOptionsWidget::set (const ToolRunnerSettings &settings)
{
  title_->setText (settings.name);
  binary_->setText (settings.binary);
  arguments_->setText (settings.arguments);
  extensions_->setText (settings.extensions.join (QLatin1String (",")));
  for (const auto i: typeMap_.keys ()) {
    i->setChecked (settings.checkTypes.contains (typeMap_[i]));
  }
}

void ToolOptionsWidget::get (ToolRunnerSettings &settings) const
{
  settings.name = title_->text ();
  settings.binary = binary_->text ();
  settings.arguments = arguments_->text ();
  settings.extensions = extensions_->text ().split (QLatin1String (","));
  for (auto &i: settings.extensions) {
    i = i.trimmed ();
  }
  settings.checkTypes = QList<int> () << CheckManual;
  for (const auto i: typeMap_.keys ()) {
    if (i->isChecked ()) {
      settings.checkTypes << typeMap_[i];
    }
  }
}

void ToolOptionsWidget::browseBinary ()
{
  auto file = QFileDialog::getOpenFileName ();
  if (!file.isEmpty ()) {
    binary_->setText (file);
  }
}

class OptionsWidget : public QWidget
{
  Q_OBJECT

  public:
    explicit OptionsWidget ();
    void set (const ToolOptionsPage::Settings &settings);
    void get (ToolOptionsPage::Settings &settings) const;

  private:
    Core::VariableChooser *variableChooser_;
    QList<ToolOptionsWidget *> toolWidgets_;
};

OptionsWidget::OptionsWidget ()
  : variableChooser_ (new Core::VariableChooser (this))
{
  auto layout = new QVBoxLayout;
  layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Expanding,
                                    QSizePolicy::Expanding));
  setLayout (layout);
}

void OptionsWidget::set (const ToolOptionsPage::Settings &settings)
{
  for (auto i = 0, end = settings.size (); i < end; ++i) {
    if (i >= toolWidgets_.size ()) {
      auto widget = new ToolOptionsWidget (variableChooser_);
      static_cast<QBoxLayout *>(layout ())->insertWidget (i, widget);
      toolWidgets_.insert (i, widget);
    }
    auto widget = toolWidgets_[i];
    widget->set (settings[i]);
  }
}

void OptionsWidget::get (ToolOptionsPage::Settings &settings) const
{
  for (auto i = 0, end = toolWidgets_.size (); i < end; ++i) {
    if (i >= settings.size ()) {
      break;
    }
    auto widget = toolWidgets_[i];
    widget->get (settings[i]);
  }
}

ToolOptionsPage::ToolOptionsPage ()
  : widget_ (nullptr)
{
  setId (OPTIONS_PAGE_ID);
  setDisplayName (tr ("Clang tools"));
  setCategory (OPTIONS_CATEGORY_ID);
  setDisplayCategory (tr ("Utilities"));
  setCategoryIcon (QLatin1String (OPTIONS_CATEGORY_ICON));

  load ();
}

QWidget * ToolOptionsPage::widget ()
{
  if (!widget_) {
    widget_ = new OptionsWidget;
  }
  widget_->set (settings_);
  return widget_.data ();
}

void ToolOptionsPage::apply ()
{
  widget_->get (settings_);
  save ();
  emit settingsSaved ();
}

void ToolOptionsPage::finish ()
{
}

const ToolOptionsPage::Settings &ToolOptionsPage::settings () const
{
  return settings_;
}

void ToolOptionsPage::load ()
{
  auto &qsettings = *(Core::ICore::settings ());
  auto loadTool =
    [&qsettings] (const QString &tool) -> ToolRunnerSettings {
      ToolRunnerSettings s;
      qsettings.beginGroup (tool);
      s.name = qsettings.value (SETTINGS_NAME, tool).toString ();
      s.binary = qsettings.value (SETTINGS_BINARY).toString ();
      auto defaultCompileDatabase = QLatin1String ("%{CurrentProject:BuildPath}/compile_commands.json");
      QString defaultArgs = QLatin1String ("-p ") + defaultCompileDatabase;
      if (s.name == NAME_CHECK) {
        defaultArgs += QStringLiteral ("-analyze -extra-arg -Xclang -extra-arg -analyzer-output=text");
      }
      s.arguments = qsettings.value (SETTINGS_ARGUMENTS, defaultArgs).toString ();
      s.extensions = qsettings.value (SETTINGS_EXTENSIONS).toString ().split (QLatin1String (","));
      auto types = qsettings.value (SETTINGS_CHECKTYPES).toString ().split (QLatin1String (","));
      for (const auto &type: types) {
        s.checkTypes << type.toInt ();
      }
      qsettings.endGroup ();
      return s;
    };

  settings_.clear ();
  qsettings.beginGroup (SETTINGS_GROUP);
  settings_ << loadTool (NAME_TIDY);
  settings_ << loadTool (NAME_MODULARIZE);
  settings_ << loadTool (NAME_CHECK);
  qsettings.endGroup ();
}

void ToolOptionsPage::save ()
{
  auto &qsettings = *(Core::ICore::settings ());
  auto saveTool =
    [&qsettings] (const ToolRunnerSettings &s) {
      qsettings.beginGroup (s.name);
      qsettings.setValue (SETTINGS_NAME, s.name);
      qsettings.setValue (SETTINGS_BINARY, s.binary);
      qsettings.setValue (SETTINGS_ARGUMENTS, s.arguments);
      qsettings.setValue (SETTINGS_EXTENSIONS, s.extensions.join (QLatin1String (",")));
      QStringList types;
      for (auto type: s.checkTypes) {
        types << QString::number (type);
      }
      qsettings.setValue (SETTINGS_CHECKTYPES, types.join (QLatin1String (",")));
      qsettings.endGroup ();
    };

  qsettings.beginGroup (SETTINGS_GROUP);
  for (const auto &i: settings_) {
    saveTool (i);
  }
  qsettings.endGroup ();
}

} // namespace ClangTools
} // namespace Internal
} // namespace QtcUtilities

#include "ToolOptionsPage.moc"
<commit_msg>Modularize removed.<commit_after>#include "ToolOptionsPage.h"
#include "ToolRunner.h"
#include "CheckTypes.h"
#include "ClangToolsConstants.h"

#include <coreplugin/icore.h>
#include <coreplugin/variablechooser.h>

#include <QWidget>
#include <QCheckBox>
#include <QLineEdit>
#include <QLabel>
#include <QBoxLayout>
#include <QPushButton>
#include <QFileDialog>

namespace QtcUtilities {
namespace Internal {
namespace ClangTools {

const QString SETTINGS_GROUP = QLatin1String ("ClangTools");
const QString SETTINGS_NAME = QLatin1String ("name");
const QString SETTINGS_BINARY = QLatin1String ("binary");
const QString SETTINGS_ARGUMENTS = QLatin1String ("arguments");
const QString SETTINGS_EXTENSIONS = QLatin1String ("extensions");
const QString SETTINGS_CHECKTYPES = QLatin1String ("checkTypes");

const QString NAME_TIDY = QLatin1String ("Tidy");
const QString NAME_CHECK = QLatin1String ("Check");

class ToolOptionsWidget : public QWidget
{
  Q_OBJECT

  public:
    explicit ToolOptionsWidget (Core::VariableChooser *variableChooser);
    void set (const ToolRunnerSettings &settings);
    void get (ToolRunnerSettings &settings) const;

  private slots:
    void browseBinary ();

  private:
    QLabel *title_;
    QLineEdit *binary_;
    QPushButton *browseBinary_;
    QLineEdit *arguments_;
    QLineEdit *extensions_;
    QCheckBox *checkOnFileAdd_;
    QCheckBox *checkOnFileChange_;
    QCheckBox *checkOnProjectBuild_;
    QCheckBox *checkOnProjectSwitch_;

    QMap<QCheckBox *, int> typeMap_;
};

ToolOptionsWidget::ToolOptionsWidget (Core::VariableChooser *variableChooser) :
  title_ (new QLabel),
  binary_ (new QLineEdit), browseBinary_ (new QPushButton (tr ("Browse..."))),
  arguments_ (new QLineEdit), extensions_ (new QLineEdit),
  checkOnFileAdd_ (new QCheckBox (tr ("Check on file add"))),
  checkOnFileChange_ (new QCheckBox (tr ("Check on file change"))),
  checkOnProjectBuild_ (new QCheckBox (tr ("Check on project build"))),
  checkOnProjectSwitch_ (new QCheckBox (tr ("Check on project switch")))
{
  auto layout = new QGridLayout;

  auto row = 0;
  layout->addWidget (title_, row, 0, 1, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Binary:")), row, 0);
  layout->addWidget (binary_, row, 1);
  layout->addWidget (browseBinary_, row, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Arguments:")), row, 0);
  layout->addWidget (arguments_, row, 1, 1, 2);

  ++row;
  layout->addWidget (new QLabel (tr ("Extensions:")), row, 0);
  layout->addWidget (extensions_, row, 1, 1, 2);

  ++row;
  layout->addWidget (checkOnFileAdd_, row, 0);
  layout->addWidget (checkOnFileChange_, row, 1, 1, 2);

  ++row;
  layout->addWidget (checkOnProjectBuild_, row, 0);
  layout->addWidget (checkOnProjectSwitch_, row, 1, 1, 2);

  setLayout (layout);

  extensions_->setToolTip (tr ("Comma separated. May be empty."));

  variableChooser->addSupportedWidget (arguments_);
  connect (browseBinary_, &QPushButton::clicked, this, &ToolOptionsWidget::browseBinary);

  typeMap_.insert (checkOnFileAdd_, CheckOnFileAdd);
  typeMap_.insert (checkOnFileChange_, CheckOnFileChange);
  typeMap_.insert (checkOnProjectBuild_, CheckOnProjectBuild);
  typeMap_.insert (checkOnProjectSwitch_, CheckOnProjectSwitch);
}

void ToolOptionsWidget::set (const ToolRunnerSettings &settings)
{
  title_->setText (settings.name);
  binary_->setText (settings.binary);
  arguments_->setText (settings.arguments);
  extensions_->setText (settings.extensions.join (QLatin1String (",")));
  for (const auto i: typeMap_.keys ()) {
    i->setChecked (settings.checkTypes.contains (typeMap_[i]));
  }
}

void ToolOptionsWidget::get (ToolRunnerSettings &settings) const
{
  settings.name = title_->text ();
  settings.binary = binary_->text ();
  settings.arguments = arguments_->text ();
  settings.extensions = extensions_->text ().split (QLatin1String (","));
  for (auto &i: settings.extensions) {
    i = i.trimmed ();
  }
  settings.checkTypes = QList<int> () << CheckManual;
  for (const auto i: typeMap_.keys ()) {
    if (i->isChecked ()) {
      settings.checkTypes << typeMap_[i];
    }
  }
}

void ToolOptionsWidget::browseBinary ()
{
  auto file = QFileDialog::getOpenFileName ();
  if (!file.isEmpty ()) {
    binary_->setText (file);
  }
}

class OptionsWidget : public QWidget
{
  Q_OBJECT

  public:
    explicit OptionsWidget ();
    void set (const ToolOptionsPage::Settings &settings);
    void get (ToolOptionsPage::Settings &settings) const;

  private:
    Core::VariableChooser *variableChooser_;
    QList<ToolOptionsWidget *> toolWidgets_;
};

OptionsWidget::OptionsWidget ()
  : variableChooser_ (new Core::VariableChooser (this))
{
  auto layout = new QVBoxLayout;
  layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Expanding,
                                    QSizePolicy::Expanding));
  setLayout (layout);
}

void OptionsWidget::set (const ToolOptionsPage::Settings &settings)
{
  for (auto i = 0, end = settings.size (); i < end; ++i) {
    if (i >= toolWidgets_.size ()) {
      auto widget = new ToolOptionsWidget (variableChooser_);
      static_cast<QBoxLayout *>(layout ())->insertWidget (i, widget);
      toolWidgets_.insert (i, widget);
    }
    auto widget = toolWidgets_[i];
    widget->set (settings[i]);
  }
}

void OptionsWidget::get (ToolOptionsPage::Settings &settings) const
{
  for (auto i = 0, end = toolWidgets_.size (); i < end; ++i) {
    if (i >= settings.size ()) {
      break;
    }
    auto widget = toolWidgets_[i];
    widget->get (settings[i]);
  }
}

ToolOptionsPage::ToolOptionsPage ()
  : widget_ (nullptr)
{
  setId (OPTIONS_PAGE_ID);
  setDisplayName (tr ("Clang tools"));
  setCategory (OPTIONS_CATEGORY_ID);
  setDisplayCategory (tr ("Utilities"));
  setCategoryIcon (QLatin1String (OPTIONS_CATEGORY_ICON));

  load ();
}

QWidget * ToolOptionsPage::widget ()
{
  if (!widget_) {
    widget_ = new OptionsWidget;
  }
  widget_->set (settings_);
  return widget_.data ();
}

void ToolOptionsPage::apply ()
{
  widget_->get (settings_);
  save ();
  emit settingsSaved ();
}

void ToolOptionsPage::finish ()
{
}

const ToolOptionsPage::Settings &ToolOptionsPage::settings () const
{
  return settings_;
}

void ToolOptionsPage::load ()
{
  auto &qsettings = *(Core::ICore::settings ());
  auto loadTool =
    [&qsettings] (const QString &tool) -> ToolRunnerSettings {
      ToolRunnerSettings s;
      qsettings.beginGroup (tool);
      s.name = qsettings.value (SETTINGS_NAME, tool).toString ();
      s.binary = qsettings.value (SETTINGS_BINARY).toString ();
      auto defaultCompileDatabase = QLatin1String ("%{CurrentProject:BuildPath}/compile_commands.json");
      QString defaultArgs = QLatin1String ("-p ") + defaultCompileDatabase;
      if (s.name == NAME_CHECK) {
        defaultArgs += QStringLiteral ("-analyze -extra-arg -Xclang -extra-arg -analyzer-output=text");
      }
      s.arguments = qsettings.value (SETTINGS_ARGUMENTS, defaultArgs).toString ();
      s.extensions = qsettings.value (SETTINGS_EXTENSIONS).toString ().split (QLatin1String (","));
      auto types = qsettings.value (SETTINGS_CHECKTYPES).toString ().split (QLatin1String (","));
      for (const auto &type: types) {
        s.checkTypes << type.toInt ();
      }
      qsettings.endGroup ();
      return s;
    };

  settings_.clear ();
  qsettings.beginGroup (SETTINGS_GROUP);
  settings_ << loadTool (NAME_TIDY);
  settings_ << loadTool (NAME_CHECK);
  qsettings.endGroup ();
}

void ToolOptionsPage::save ()
{
  auto &qsettings = *(Core::ICore::settings ());
  auto saveTool =
    [&qsettings] (const ToolRunnerSettings &s) {
      qsettings.beginGroup (s.name);
      qsettings.setValue (SETTINGS_NAME, s.name);
      qsettings.setValue (SETTINGS_BINARY, s.binary);
      qsettings.setValue (SETTINGS_ARGUMENTS, s.arguments);
      qsettings.setValue (SETTINGS_EXTENSIONS, s.extensions.join (QLatin1String (",")));
      QStringList types;
      for (auto type: s.checkTypes) {
        types << QString::number (type);
      }
      qsettings.setValue (SETTINGS_CHECKTYPES, types.join (QLatin1String (",")));
      qsettings.endGroup ();
    };

  qsettings.beginGroup (SETTINGS_GROUP);
  for (const auto &i: settings_) {
    saveTool (i);
  }
  qsettings.endGroup ();
}

} // namespace ClangTools
} // namespace Internal
} // namespace QtcUtilities

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


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/plugins/plugin_list.h"

#include "base/file_util.h"
#include "base/path_service.h"

namespace NPAPI {

void PluginList::PlatformInit() {
}

void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
  // Mozilla code to reference:
  // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
  // and tens of accompanying files (mxr is very helpful).
  // This code carefully matches their behavior for compat reasons.

  // 1) MOZ_PLUGIN_PATH env variable.
  const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
  if (moz_plugin_path)
    plugin_dirs->push_back(FilePath(moz_plugin_path));

  // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
  // TODO(evanm): should we do anything here?

  // 3) NS_APP_PLUGINS_DIR: the binary dir + "plugins/".
  FilePath dir;
  PathService::Get(base::DIR_EXE, &dir);
  plugin_dirs->push_back(dir.Append("plugins"));

  // 4) NS_SYSTEM_PLUGINS_DIR:
  // TODO(evanm): when we support 64-bit platforms, we'll need to fix this
  // to be conditional.
  COMPILE_ASSERT(sizeof(int)==4, fix_system_lib_path);
  plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
}

void PluginList::LoadPluginsFromDir(const FilePath& path) {
  file_util::FileEnumerator enumerator(path,
                                       false, // not recursive
                                       file_util::FileEnumerator::FILES);
  for (FilePath path = enumerator.Next(); !path.value().empty();
       path = enumerator.Next()) {
    // Skip over Mozilla .xpt files.
    if (!path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
      LoadPlugin(path);
  }
}

bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info) {
  // The equivalent Windows code verifies we haven't loaded a newer version
  // of the same plugin, and then blacklists some known bad plugins.
  // The equivalent Mac code verifies that plugins encountered first in the
  // plugin list clobber later entries.
  // TODO(evanm): figure out which behavior is appropriate for Linux.
  // We don't need either yet as I'm just testing with Flash for now.
  return true;
}

void PluginList::LoadInternalPlugins() {
  // none for now
}

} // namespace NPAPI
<commit_msg>linux: also look in ~/.mozilla/plugins for plugins<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/plugins/plugin_list.h"

#include "base/file_util.h"
#include "base/path_service.h"

namespace NPAPI {

void PluginList::PlatformInit() {
}

void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
  // Mozilla code to reference:
  // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
  // and tens of accompanying files (mxr is very helpful).
  // This code carefully matches their behavior for compat reasons.

  // 1) MOZ_PLUGIN_PATH env variable.
  const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
  if (moz_plugin_path)
    plugin_dirs->push_back(FilePath(moz_plugin_path));

  // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
  // This is a de-facto standard, so even though we're not Mozilla, let's
  // look in there too.
  const char* home = getenv("HOME");
  if (home)
    plugin_dirs->push_back(FilePath(home).Append(".mozilla/plugins"));
  // TODO(evanm): maybe consult our own plugins dir, like
  // ~/.config/chromium/Plugins?

  // 3) NS_APP_PLUGINS_DIR: the binary dir + "plugins/".
  FilePath dir;
  PathService::Get(base::DIR_EXE, &dir);
  plugin_dirs->push_back(dir.Append("plugins"));

  // 4) NS_SYSTEM_PLUGINS_DIR:
  // TODO(evanm): when we support 64-bit platforms, we'll need to fix this
  // to be conditional.
  COMPILE_ASSERT(sizeof(int)==4, fix_system_lib_path);
  plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
}

void PluginList::LoadPluginsFromDir(const FilePath& path) {
  file_util::FileEnumerator enumerator(path,
                                       false, // not recursive
                                       file_util::FileEnumerator::FILES);
  for (FilePath path = enumerator.Next(); !path.value().empty();
       path = enumerator.Next()) {
    // Skip over Mozilla .xpt files.
    if (!path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
      LoadPlugin(path);
  }
}

bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info) {
  // The equivalent Windows code verifies we haven't loaded a newer version
  // of the same plugin, and then blacklists some known bad plugins.
  // The equivalent Mac code verifies that plugins encountered first in the
  // plugin list clobber later entries.
  // TODO(evanm): figure out which behavior is appropriate for Linux.
  // We don't need either yet as I'm just testing with Flash for now.
  return true;
}

void PluginList::LoadInternalPlugins() {
  // none for now
}

} // namespace NPAPI
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/profile_sync_service.h"

#include <stack>
#include <vector>

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/common/time_format.h"
#include "grit/generated_resources.h"
#include "views/window/window.h"

using browser_sync::ChangeProcessor;
using browser_sync::ModelAssociator;
using browser_sync::SyncBackendHost;

typedef GoogleServiceAuthError AuthError;

// Default sync server URL.
static const char kSyncServerUrl[] = "https://clients4.google.com/chrome-sync";

ProfileSyncService::ProfileSyncService(Profile* profile)
    : last_auth_error_(AuthError::None()),
      profile_(profile),
      sync_service_url_(kSyncServerUrl),
      backend_initialized_(false),
      expecting_first_run_auth_needed_event_(false),
      is_auth_in_progress_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
      unrecoverable_error_detected_(false) {
  change_processor_.reset(new ChangeProcessor(this));
}

ProfileSyncService::~ProfileSyncService() {
  Shutdown(false);
}

void ProfileSyncService::set_model_associator(
    browser_sync::ModelAssociator* associator) {
  model_associator_ = associator;
  change_processor_->set_model_associator(associator);
}

void ProfileSyncService::Initialize() {
  InitSettings();
  RegisterPreferences();
  if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
    DisableForUser();  // Clean up in case of previous crash / setup abort.
  else
    StartUp();
}

void ProfileSyncService::InitSettings() {
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();

  // Override the sync server URL from the command-line, if sync server
  // command-line argument exists.
  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
    std::string value(command_line.GetSwitchValueASCII(
        switches::kSyncServiceURL));
    if (!value.empty()) {
      GURL custom_sync_url(value);
      if (custom_sync_url.is_valid()) {
        sync_service_url_ = custom_sync_url;
      } else {
        LOG(WARNING) << "The following sync URL specified at the command-line "
                     << "is invalid: " << value;
      }
    }
  }
}

void ProfileSyncService::RegisterPreferences() {
  PrefService* pref_service = profile_->GetPrefs();
  if (pref_service->IsPrefRegistered(prefs::kSyncLastSyncedTime))
    return;
  pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
  pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
}

void ProfileSyncService::ClearPreferences() {
  PrefService* pref_service = profile_->GetPrefs();
  pref_service->ClearPref(prefs::kSyncLastSyncedTime);
  pref_service->ClearPref(prefs::kSyncHasSetupCompleted);

  pref_service->ScheduleSavePersistentPrefs();
}

void ProfileSyncService::InitializeBackend() {
  backend_->Initialize(sync_service_url_, profile_->GetRequestContext());
}

void ProfileSyncService::StartUp() {
  // Don't start up multiple times.
  if (backend_.get())
    return;

  last_synced_time_ = base::Time::FromInternalValue(
      profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime));

  backend_.reset(new SyncBackendHost(this, profile_->GetPath(),
                                     change_processor_.get()));

  registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED,
                 Source<Profile>(profile_));

  // Create new model association manager and change processor.
  model_associator_ = new ModelAssociator(this);
  change_processor_->set_model_associator(model_associator_);

  InitializeBackend();
}

void ProfileSyncService::Shutdown(bool sync_disabled) {
  registrar_.RemoveAll();

  if (backend_.get())
    backend_->Shutdown(sync_disabled);

  change_processor_->Stop();
  backend_.reset();

  // Clear all associations and throw away the association manager instance.
  if (model_associator_.get()) {
    model_associator_->ClearAll();
    model_associator_ = NULL;
  }

  // Clear various flags.
  is_auth_in_progress_ = false;
  backend_initialized_ = false;
  expecting_first_run_auth_needed_event_ = false;
  last_attempted_user_email_.clear();
}

void ProfileSyncService::EnableForUser() {
  if (WizardIsVisible()) {
    // TODO(timsteele): Focus wizard.
    return;
  }
  expecting_first_run_auth_needed_event_ = true;

  StartUp();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::DisableForUser() {
  if (WizardIsVisible()) {
    // TODO(timsteele): Focus wizard.
    return;
  }
  Shutdown(true);
  ClearPreferences();

  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::Observe(NotificationType type,
                                 const NotificationSource& source,
                                 const NotificationDetails& details) {
  DCHECK_EQ(NotificationType::BOOKMARK_MODEL_LOADED, type.value);
  registrar_.RemoveAll();
  StartProcessingChangesIfReady();
}

bool ProfileSyncService::MergeAndSyncAcceptanceNeeded() const {
  // If we've shown the dialog before, don't show it again.
  if (profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
    return false;

  return model_associator_->BookmarkModelHasUserCreatedNodes() &&
         model_associator_->SyncModelHasUserCreatedNodes();
}

bool ProfileSyncService::HasSyncSetupCompleted() const {
  return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted);
}

void ProfileSyncService::SetSyncSetupCompleted() {
  PrefService* prefs = profile()->GetPrefs();
  prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true);
  prefs->ScheduleSavePersistentPrefs();
}

void ProfileSyncService::UpdateLastSyncedTime() {
  last_synced_time_ = base::Time::Now();
  profile_->GetPrefs()->SetInt64(prefs::kSyncLastSyncedTime,
      last_synced_time_.ToInternalValue());
  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
}

// An invariant has been violated.  Transition to an error state where we try
// to do as little work as possible, to avoid further corruption or crashes.
void ProfileSyncService::OnUnrecoverableError() {
  unrecoverable_error_detected_ = true;
  change_processor_->Stop();

  // Tell the wizard so it can inform the user only if it is already open.
  wizard_.Step(SyncSetupWizard::FATAL_ERROR);
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
  LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable.";
}

void ProfileSyncService::OnBackendInitialized() {
  backend_initialized_ = true;
  StartProcessingChangesIfReady();

  // The very first time the backend initializes is effectively the first time
  // we can say we successfully "synced".  last_synced_time_ will only be null
  // in this case, because the pref wasn't restored on StartUp.
  if (last_synced_time_.is_null())
    UpdateLastSyncedTime();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnSyncCycleCompleted() {
  UpdateLastSyncedTime();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnAuthError() {
  last_auth_error_ = backend_->GetAuthError();
  // Protect against the in-your-face dialogs that pop out of nowhere.
  // Require the user to click somewhere to run the setup wizard in the case
  // of a steady-state auth failure.
  if (WizardIsVisible() || expecting_first_run_auth_needed_event_) {
    wizard_.Step(AuthError::NONE == last_auth_error_.state() ?
        SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN);
  }

  if (expecting_first_run_auth_needed_event_) {
    last_auth_error_ = AuthError::None();
    expecting_first_run_auth_needed_event_ = false;
  }

  if (!WizardIsVisible()) {
    auth_error_time_ == base::TimeTicks::Now();
  }

  if (!auth_start_time_.is_null()) {
    UMA_HISTOGRAM_TIMES("Sync.AuthorizationTimeInNetwork",
                    base::TimeTicks::Now() - auth_start_time_);
    auth_start_time_ = base::TimeTicks();
  }

  is_auth_in_progress_ = false;
  // Fan the notification out to interested UI-thread components.
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::ShowLoginDialog() {
  if (WizardIsVisible())
    return;

  if (!auth_error_time_.is_null()) {
    UMA_HISTOGRAM_LONG_TIMES("Sync.ReauthorizationTime",
                             base::TimeTicks::Now() - auth_error_time_);
    auth_error_time_ = base::TimeTicks();  // Reset auth_error_time_ to null.
  }

  if (last_auth_error_.state() != AuthError::NONE) {
    wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
  }
}

SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
  return backend_->GetStatusSummary();
}

SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() {
  return backend_->GetDetailedStatus();
}

std::wstring ProfileSyncService::BuildSyncStatusSummaryText(
  const sync_api::SyncManager::Status::Summary& summary) {
  switch (summary) {
    case sync_api::SyncManager::Status::OFFLINE:
      return L"OFFLINE";
    case sync_api::SyncManager::Status::OFFLINE_UNSYNCED:
      return L"OFFLINE_UNSYNCED";
    case sync_api::SyncManager::Status::SYNCING:
      return L"SYNCING";
    case sync_api::SyncManager::Status::READY:
      return L"READY";
    case sync_api::SyncManager::Status::CONFLICT:
      return L"CONFLICT";
    case sync_api::SyncManager::Status::OFFLINE_UNUSABLE:
      return L"OFFLINE_UNUSABLE";
    case sync_api::SyncManager::Status::INVALID:  // fall through
    default:
      return L"UNKNOWN";
  }
}

std::wstring ProfileSyncService::GetLastSyncedTimeString() const {
  if (last_synced_time_.is_null())
    return l10n_util::GetString(IDS_SYNC_TIME_NEVER);

  base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;

  if (last_synced < base::TimeDelta::FromMinutes(1))
    return l10n_util::GetString(IDS_SYNC_TIME_JUST_NOW);

  return TimeFormat::TimeElapsed(last_synced);
}

string16 ProfileSyncService::GetAuthenticatedUsername() const {
  return backend_->GetAuthenticatedUsername();
}

void ProfileSyncService::OnUserSubmittedAuth(
    const std::string& username, const std::string& password,
    const std::string& captcha) {
  last_attempted_user_email_ = username;
  is_auth_in_progress_ = true;
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());

  auth_start_time_ = base::TimeTicks::Now();
  backend_->Authenticate(username, password, captcha);
}

void ProfileSyncService::OnUserAcceptedMergeAndSync() {
  base::TimeTicks start_time = base::TimeTicks::Now();
  bool not_first_run = model_associator_->SyncModelHasUserCreatedNodes();
  bool merge_success = model_associator_->AssociateModels();
  UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedBookmarkAssociation",
                             base::TimeTicks::Now() - start_time);
  if (!merge_success) {
    LOG(ERROR) << "Model association failed.";
    OnUnrecoverableError();
    return;
  }

  wizard_.Step(not_first_run ? SyncSetupWizard::DONE :
               SyncSetupWizard::DONE_FIRST_TIME);

  change_processor_->Start(profile_->GetBookmarkModel(),
                           backend_->GetUserShareHandle());
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnUserCancelledDialog() {
  if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) {
    // A sync dialog was aborted before authentication or merge acceptance.
    // Rollback.
    DisableForUser();
  }

  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::StartProcessingChangesIfReady() {
  BookmarkModel* model = profile_->GetBookmarkModel();

  DCHECK(!change_processor_->IsRunning());

  // First check if the subsystems are ready.  We can't proceed until they
  // both have finished loading.
  if (!model->IsLoaded())
    return;
  if (!backend_initialized_)
    return;

  // Show the sync merge warning dialog if needed.
  if (MergeAndSyncAcceptanceNeeded()) {
    ProfileSyncService::SyncEvent(MERGE_AND_SYNC_NEEDED);
    wizard_.Step(SyncSetupWizard::MERGE_AND_SYNC);
    return;
  }

  // We're ready to merge the models.
  base::TimeTicks start_time = base::TimeTicks::Now();
  bool not_first_run = model_associator_->SyncModelHasUserCreatedNodes();
  bool merge_success = model_associator_->AssociateModels();
  UMA_HISTOGRAM_TIMES("Sync.BookmarkAssociationTime",
                      base::TimeTicks::Now() - start_time);
  if (!merge_success) {
      LOG(ERROR) << "Model association failed.";
      OnUnrecoverableError();
      return;
  }

  wizard_.Step(not_first_run ? SyncSetupWizard::DONE :
               SyncSetupWizard::DONE_FIRST_TIME);

  change_processor_->Start(profile_->GetBookmarkModel(),
                           backend_->GetUserShareHandle());
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void ProfileSyncService::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void ProfileSyncService::SyncEvent(SyncEventCodes code) {
  static LinearHistogram histogram("Sync.EventCodes", MIN_SYNC_EVENT_CODE,
                                   MAX_SYNC_EVENT_CODE - 1,
                                   MAX_SYNC_EVENT_CODE);
  histogram.SetFlags(kUmaTargetedHistogramFlag);
  histogram.Add(code);
}

bool ProfileSyncService::IsSyncEnabled() {
#if defined(OS_POSIX)
  return CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableSync);
#else
  return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync);
#endif
}

bool ProfileSyncService::ShouldPushChanges() {
  // True only after all bootstrapping has succeeded: the bookmark model is
  // loaded, the sync backend is initialized, the two domains are
  // consistent with one another, and no unrecoverable error has transpired.
  return change_processor_->IsRunning();
}
<commit_msg>Enable sync by default on all platforms.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/profile_sync_service.h"

#include <stack>
#include <vector>

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/common/time_format.h"
#include "grit/generated_resources.h"
#include "views/window/window.h"

using browser_sync::ChangeProcessor;
using browser_sync::ModelAssociator;
using browser_sync::SyncBackendHost;

typedef GoogleServiceAuthError AuthError;

// Default sync server URL.
static const char kSyncServerUrl[] = "https://clients4.google.com/chrome-sync";

ProfileSyncService::ProfileSyncService(Profile* profile)
    : last_auth_error_(AuthError::None()),
      profile_(profile),
      sync_service_url_(kSyncServerUrl),
      backend_initialized_(false),
      expecting_first_run_auth_needed_event_(false),
      is_auth_in_progress_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
      unrecoverable_error_detected_(false) {
  change_processor_.reset(new ChangeProcessor(this));
}

ProfileSyncService::~ProfileSyncService() {
  Shutdown(false);
}

void ProfileSyncService::set_model_associator(
    browser_sync::ModelAssociator* associator) {
  model_associator_ = associator;
  change_processor_->set_model_associator(associator);
}

void ProfileSyncService::Initialize() {
  InitSettings();
  RegisterPreferences();
  if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
    DisableForUser();  // Clean up in case of previous crash / setup abort.
  else
    StartUp();
}

void ProfileSyncService::InitSettings() {
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();

  // Override the sync server URL from the command-line, if sync server
  // command-line argument exists.
  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
    std::string value(command_line.GetSwitchValueASCII(
        switches::kSyncServiceURL));
    if (!value.empty()) {
      GURL custom_sync_url(value);
      if (custom_sync_url.is_valid()) {
        sync_service_url_ = custom_sync_url;
      } else {
        LOG(WARNING) << "The following sync URL specified at the command-line "
                     << "is invalid: " << value;
      }
    }
  }
}

void ProfileSyncService::RegisterPreferences() {
  PrefService* pref_service = profile_->GetPrefs();
  if (pref_service->IsPrefRegistered(prefs::kSyncLastSyncedTime))
    return;
  pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
  pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
}

void ProfileSyncService::ClearPreferences() {
  PrefService* pref_service = profile_->GetPrefs();
  pref_service->ClearPref(prefs::kSyncLastSyncedTime);
  pref_service->ClearPref(prefs::kSyncHasSetupCompleted);

  pref_service->ScheduleSavePersistentPrefs();
}

void ProfileSyncService::InitializeBackend() {
  backend_->Initialize(sync_service_url_, profile_->GetRequestContext());
}

void ProfileSyncService::StartUp() {
  // Don't start up multiple times.
  if (backend_.get())
    return;

  last_synced_time_ = base::Time::FromInternalValue(
      profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime));

  backend_.reset(new SyncBackendHost(this, profile_->GetPath(),
                                     change_processor_.get()));

  registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED,
                 Source<Profile>(profile_));

  // Create new model association manager and change processor.
  model_associator_ = new ModelAssociator(this);
  change_processor_->set_model_associator(model_associator_);

  InitializeBackend();
}

void ProfileSyncService::Shutdown(bool sync_disabled) {
  registrar_.RemoveAll();

  if (backend_.get())
    backend_->Shutdown(sync_disabled);

  change_processor_->Stop();
  backend_.reset();

  // Clear all associations and throw away the association manager instance.
  if (model_associator_.get()) {
    model_associator_->ClearAll();
    model_associator_ = NULL;
  }

  // Clear various flags.
  is_auth_in_progress_ = false;
  backend_initialized_ = false;
  expecting_first_run_auth_needed_event_ = false;
  last_attempted_user_email_.clear();
}

void ProfileSyncService::EnableForUser() {
  if (WizardIsVisible()) {
    // TODO(timsteele): Focus wizard.
    return;
  }
  expecting_first_run_auth_needed_event_ = true;

  StartUp();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::DisableForUser() {
  if (WizardIsVisible()) {
    // TODO(timsteele): Focus wizard.
    return;
  }
  Shutdown(true);
  ClearPreferences();

  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::Observe(NotificationType type,
                                 const NotificationSource& source,
                                 const NotificationDetails& details) {
  DCHECK_EQ(NotificationType::BOOKMARK_MODEL_LOADED, type.value);
  registrar_.RemoveAll();
  StartProcessingChangesIfReady();
}

bool ProfileSyncService::MergeAndSyncAcceptanceNeeded() const {
  // If we've shown the dialog before, don't show it again.
  if (profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
    return false;

  return model_associator_->BookmarkModelHasUserCreatedNodes() &&
         model_associator_->SyncModelHasUserCreatedNodes();
}

bool ProfileSyncService::HasSyncSetupCompleted() const {
  return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted);
}

void ProfileSyncService::SetSyncSetupCompleted() {
  PrefService* prefs = profile()->GetPrefs();
  prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true);
  prefs->ScheduleSavePersistentPrefs();
}

void ProfileSyncService::UpdateLastSyncedTime() {
  last_synced_time_ = base::Time::Now();
  profile_->GetPrefs()->SetInt64(prefs::kSyncLastSyncedTime,
      last_synced_time_.ToInternalValue());
  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
}

// An invariant has been violated.  Transition to an error state where we try
// to do as little work as possible, to avoid further corruption or crashes.
void ProfileSyncService::OnUnrecoverableError() {
  unrecoverable_error_detected_ = true;
  change_processor_->Stop();

  // Tell the wizard so it can inform the user only if it is already open.
  wizard_.Step(SyncSetupWizard::FATAL_ERROR);
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
  LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable.";
}

void ProfileSyncService::OnBackendInitialized() {
  backend_initialized_ = true;
  StartProcessingChangesIfReady();

  // The very first time the backend initializes is effectively the first time
  // we can say we successfully "synced".  last_synced_time_ will only be null
  // in this case, because the pref wasn't restored on StartUp.
  if (last_synced_time_.is_null())
    UpdateLastSyncedTime();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnSyncCycleCompleted() {
  UpdateLastSyncedTime();
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnAuthError() {
  last_auth_error_ = backend_->GetAuthError();
  // Protect against the in-your-face dialogs that pop out of nowhere.
  // Require the user to click somewhere to run the setup wizard in the case
  // of a steady-state auth failure.
  if (WizardIsVisible() || expecting_first_run_auth_needed_event_) {
    wizard_.Step(AuthError::NONE == last_auth_error_.state() ?
        SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN);
  }

  if (expecting_first_run_auth_needed_event_) {
    last_auth_error_ = AuthError::None();
    expecting_first_run_auth_needed_event_ = false;
  }

  if (!WizardIsVisible()) {
    auth_error_time_ == base::TimeTicks::Now();
  }

  if (!auth_start_time_.is_null()) {
    UMA_HISTOGRAM_TIMES("Sync.AuthorizationTimeInNetwork",
                    base::TimeTicks::Now() - auth_start_time_);
    auth_start_time_ = base::TimeTicks();
  }

  is_auth_in_progress_ = false;
  // Fan the notification out to interested UI-thread components.
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::ShowLoginDialog() {
  if (WizardIsVisible())
    return;

  if (!auth_error_time_.is_null()) {
    UMA_HISTOGRAM_LONG_TIMES("Sync.ReauthorizationTime",
                             base::TimeTicks::Now() - auth_error_time_);
    auth_error_time_ = base::TimeTicks();  // Reset auth_error_time_ to null.
  }

  if (last_auth_error_.state() != AuthError::NONE) {
    wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
  }
}

SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
  return backend_->GetStatusSummary();
}

SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() {
  return backend_->GetDetailedStatus();
}

std::wstring ProfileSyncService::BuildSyncStatusSummaryText(
  const sync_api::SyncManager::Status::Summary& summary) {
  switch (summary) {
    case sync_api::SyncManager::Status::OFFLINE:
      return L"OFFLINE";
    case sync_api::SyncManager::Status::OFFLINE_UNSYNCED:
      return L"OFFLINE_UNSYNCED";
    case sync_api::SyncManager::Status::SYNCING:
      return L"SYNCING";
    case sync_api::SyncManager::Status::READY:
      return L"READY";
    case sync_api::SyncManager::Status::CONFLICT:
      return L"CONFLICT";
    case sync_api::SyncManager::Status::OFFLINE_UNUSABLE:
      return L"OFFLINE_UNUSABLE";
    case sync_api::SyncManager::Status::INVALID:  // fall through
    default:
      return L"UNKNOWN";
  }
}

std::wstring ProfileSyncService::GetLastSyncedTimeString() const {
  if (last_synced_time_.is_null())
    return l10n_util::GetString(IDS_SYNC_TIME_NEVER);

  base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;

  if (last_synced < base::TimeDelta::FromMinutes(1))
    return l10n_util::GetString(IDS_SYNC_TIME_JUST_NOW);

  return TimeFormat::TimeElapsed(last_synced);
}

string16 ProfileSyncService::GetAuthenticatedUsername() const {
  return backend_->GetAuthenticatedUsername();
}

void ProfileSyncService::OnUserSubmittedAuth(
    const std::string& username, const std::string& password,
    const std::string& captcha) {
  last_attempted_user_email_ = username;
  is_auth_in_progress_ = true;
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());

  auth_start_time_ = base::TimeTicks::Now();
  backend_->Authenticate(username, password, captcha);
}

void ProfileSyncService::OnUserAcceptedMergeAndSync() {
  base::TimeTicks start_time = base::TimeTicks::Now();
  bool not_first_run = model_associator_->SyncModelHasUserCreatedNodes();
  bool merge_success = model_associator_->AssociateModels();
  UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedBookmarkAssociation",
                             base::TimeTicks::Now() - start_time);
  if (!merge_success) {
    LOG(ERROR) << "Model association failed.";
    OnUnrecoverableError();
    return;
  }

  wizard_.Step(not_first_run ? SyncSetupWizard::DONE :
               SyncSetupWizard::DONE_FIRST_TIME);

  change_processor_->Start(profile_->GetBookmarkModel(),
                           backend_->GetUserShareHandle());
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::OnUserCancelledDialog() {
  if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) {
    // A sync dialog was aborted before authentication or merge acceptance.
    // Rollback.
    DisableForUser();
  }

  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::StartProcessingChangesIfReady() {
  BookmarkModel* model = profile_->GetBookmarkModel();

  DCHECK(!change_processor_->IsRunning());

  // First check if the subsystems are ready.  We can't proceed until they
  // both have finished loading.
  if (!model->IsLoaded())
    return;
  if (!backend_initialized_)
    return;

  // Show the sync merge warning dialog if needed.
  if (MergeAndSyncAcceptanceNeeded()) {
    ProfileSyncService::SyncEvent(MERGE_AND_SYNC_NEEDED);
    wizard_.Step(SyncSetupWizard::MERGE_AND_SYNC);
    return;
  }

  // We're ready to merge the models.
  base::TimeTicks start_time = base::TimeTicks::Now();
  bool not_first_run = model_associator_->SyncModelHasUserCreatedNodes();
  bool merge_success = model_associator_->AssociateModels();
  UMA_HISTOGRAM_TIMES("Sync.BookmarkAssociationTime",
                      base::TimeTicks::Now() - start_time);
  if (!merge_success) {
      LOG(ERROR) << "Model association failed.";
      OnUnrecoverableError();
      return;
  }

  wizard_.Step(not_first_run ? SyncSetupWizard::DONE :
               SyncSetupWizard::DONE_FIRST_TIME);

  change_processor_->Start(profile_->GetBookmarkModel(),
                           backend_->GetUserShareHandle());
  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}

void ProfileSyncService::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void ProfileSyncService::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void ProfileSyncService::SyncEvent(SyncEventCodes code) {
  static LinearHistogram histogram("Sync.EventCodes", MIN_SYNC_EVENT_CODE,
                                   MAX_SYNC_EVENT_CODE - 1,
                                   MAX_SYNC_EVENT_CODE);
  histogram.SetFlags(kUmaTargetedHistogramFlag);
  histogram.Add(code);
}

bool ProfileSyncService::IsSyncEnabled() {
  // We have switches::kEnableSync just in case we need to change back to
  // sync-disabled-by-default on a platform.
  return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync);
}

bool ProfileSyncService::ShouldPushChanges() {
  // True only after all bootstrapping has succeeded: the bookmark model is
  // loaded, the sync backend is initialized, the two domains are
  // consistent with one another, and no unrecoverable error has transpired.
  return change_processor_->IsRunning();
}
<|endoftext|>