<commit_before>/***************************************************************************//**
 * FILE : block_array.hpp
 *
 * Implements an array container allocated in blocks.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#if !defined (BLOCK_ARRAY_HPP)
#define BLOCK_ARRAY_HPP

#include "general/debug.h"

template <typename IndexType, typename EntryType, int blockLength = 256 >
	class block_array
{
private:
	// Note: any new attributes must be handled by swap()
	EntryType **blocks;
	IndexType blockCount;

	EntryType* getOrCreateBlock(IndexType blockIndex)
	{
		if (blockIndex >= blockCount)
		{
			IndexType newBlockCount = blockIndex + 1;
			if (newBlockCount < blockCount*2)
			{
				newBlockCount = blockCount*2;
			}
			EntryType **newBlocks;
			if (!REALLOCATE(newBlocks, blocks, EntryType *, newBlockCount))
				return NULL;
			for (IndexType i = blockCount; i < newBlockCount; i++)
			{
				newBlocks[i] = NULL;
			}
			blocks = newBlocks;
			blockCount = newBlockCount;
		}
		EntryType *block = blocks[blockIndex];
		if (!block)
		{
			if (ALLOCATE(block, EntryType, blockLength))
			{
				for (IndexType i = 0; i < blockLength; i++)
				{
					block[i] = 0; // only works for numeric or pointer types
				}
				blocks[blockIndex] = block;
			}
		}
		return block;
	}

public:

	block_array() :
		blocks(NULL),
		blockCount(0)
	{
	}

	~block_array()
	{
		clear();
	}

	void clear()
	{
		for (IndexType i = 0; i < blockCount; i++)
		{
			if (blocks[i])
			{
				DEALLOCATE(blocks[i]);
			}
		}
		if (blocks)
		{
			DEALLOCATE(blocks);
		}
		blockCount = 0;
	}

	/** Swaps all data with other block_array. Cannot fail. */
	void swap(block_array& other)
	{
		EntryType **temp_blocks = blocks;
		IndexType temp_blockCount = blockCount;
		blocks = other.blocks;
		blockCount = other.blockCount;
		other.blocks = temp_blocks;
		other.blockCount = temp_blockCount;
	}

	/**
	 * Get a value from the block_array.
	 * @param index  The index of the value to retrieve, starting at 0.
	 * @param value  On success, filled with value held at index.
	 * @return  1 if value returned, 0 if no value at index.
	 */
	int getValue(IndexType index, EntryType& value) const
	{
		IndexType blockIndex = index / blockLength;
		if (blockIndex < blockCount)
		{
			EntryType *block = blocks[blockIndex];
			if (block)
			{
				IndexType entryIndex = index % blockLength;
				value = block[entryIndex];
				return 1;
			}
		}
		return 0;
	}

	/**
	 * Set a value in the block_array.
	 * @param index  The index of the value to set, starting at 0.
	 * @param value  Value to set at index.
	 * @return  1 if value set, 0 if failed.
	 */
	int setValue(IndexType index, EntryType value)
	{
		IndexType blockIndex = index / blockLength;
		EntryType* block = getOrCreateBlock(blockIndex);
		if (!block)
			return 0;
		IndexType entryIndex = index % blockLength;
		block[entryIndex] = value;
		return 1;
	}

	bool setValues(IndexType minIndex, IndexType maxIndex, EntryType value)
	{
		// GRC: can be made faster
		for (IndexType index = minIndex; index <= maxIndex; index++)
		{
			if (!setValue(index, value))
				return false;
		}
		return true;
	}

};

/** stores boolean values as individual bits, with no value equivalent to false */
template <typename IndexType, int intBlockLength = 32>
	class bool_array : private block_array<IndexType, unsigned int, intBlockLength>
{
public:
	void clear()
	{
		block_array<IndexType, unsigned int, intBlockLength>::clear();
	}

	void swap(bool_array& other)
	{
		block_array<IndexType, unsigned int, intBlockLength>::swap(other);
	}

	/** @param oldValue  Returns old value so client can determine if status changed */
	int setBool(IndexType index, bool value, bool& oldValue)
	{
		IndexType intIndex = index >> 5;
		unsigned int intValue = 0;
		int hasValue = getValue(intIndex, intValue);
		if (hasValue || value)
		{
			unsigned int mask = (1 << (index & 0x1F));
			oldValue = (0 != (intValue & mask));
			if (oldValue != value)
			{
				return setValue(intIndex, intValue ^ mask);
			}
		}
		else
		{
			oldValue = false;
		}
		return 1;
	}

	bool getBool(IndexType index) const
	{
		IndexType intIndex = index >> 5;
		unsigned int intValue = 0;
		if (getValue(intIndex, intValue))
		{
			unsigned int mask = (1 << (index & 0x1F));
			return (0 != (intValue & mask));
		}
		return false;
	}

	/**
	 * @param lastTrueIndex  Updated to equal or next lower index with true value.
	 * @return  true if found, false if none.
	 */
	bool updateLastTrueIndex(IndexType& lastTrueIndex)
	{
		// GRC this can be made much more efficient
		while (!getBool(lastTrueIndex))
		{
			--lastTrueIndex;
			if (lastTrueIndex < 0)
				return false;
		}
		return true;
	}

	/** @return  true if values for all indexes in range are true; false otherwise */
	bool isRangeTrue(IndexType minIndex, IndexType maxIndex)
	{
		if (minIndex > maxIndex)
			return false;
		// GRC this can be made much more efficient
		IndexType index = minIndex;
		while (getBool(index))
		{
			if (index == maxIndex)
				return true;
			index++;
		}
		return false;
	}

	/** Sets all entries from index 0..indexCount-1 to true.
	 * @return  true if completely successful, false otherwise */
	bool setAllTrue(IndexType indexCount)
	{
		IndexType intIndexCount = indexCount >> 5;
		// bulk set the flags in lots of 32 bits
		if (!setValues(0, intIndexCount-1, 0xFFFFFFFF))
			return false;
		// individually set remaining bits
		for (IndexType index = intIndexCount*32; index < indexCount; index++)
		{
			bool oldValue;
			if (!setBool(index, true, oldValue))
				return false;
		}
		return true;
	}
};

#endif /* !defined (BLOCK_ARRAY_HPP) */
<commit_msg>Compilation fixes for gcc 4.7.1.<commit_after>/***************************************************************************//**
 * FILE : block_array.hpp
 *
 * Implements an array container allocated in blocks.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#if !defined (BLOCK_ARRAY_HPP)
#define BLOCK_ARRAY_HPP

#include "general/debug.h"

template <typename IndexType, typename EntryType, int blockLength = 256 >
	class block_array
{
private:
	// Note: any new attributes must be handled by swap()
	EntryType **blocks;
	IndexType blockCount;

	EntryType* getOrCreateBlock(IndexType blockIndex)
	{
		if (blockIndex >= blockCount)
		{
			IndexType newBlockCount = blockIndex + 1;
			if (newBlockCount < blockCount*2)
			{
				newBlockCount = blockCount*2;
			}
			EntryType **newBlocks;
			if (!REALLOCATE(newBlocks, blocks, EntryType *, newBlockCount))
				return NULL;
			for (IndexType i = blockCount; i < newBlockCount; i++)
			{
				newBlocks[i] = NULL;
			}
			blocks = newBlocks;
			blockCount = newBlockCount;
		}
		EntryType *block = blocks[blockIndex];
		if (!block)
		{
			if (ALLOCATE(block, EntryType, blockLength))
			{
				for (IndexType i = 0; i < blockLength; i++)
				{
					block[i] = 0; // only works for numeric or pointer types
				}
				blocks[blockIndex] = block;
			}
		}
		return block;
	}

public:

	block_array() :
		blocks(NULL),
		blockCount(0)
	{
	}

	~block_array()
	{
		clear();
	}

	void clear()
	{
		for (IndexType i = 0; i < blockCount; i++)
		{
			if (blocks[i])
			{
				DEALLOCATE(blocks[i]);
			}
		}
		if (blocks)
		{
			DEALLOCATE(blocks);
		}
		blockCount = 0;
	}

	/** Swaps all data with other block_array. Cannot fail. */
	void swap(block_array& other)
	{
		EntryType **temp_blocks = blocks;
		IndexType temp_blockCount = blockCount;
		blocks = other.blocks;
		blockCount = other.blockCount;
		other.blocks = temp_blocks;
		other.blockCount = temp_blockCount;
	}

	/**
	 * Get a value from the block_array.
	 * @param index  The index of the value to retrieve, starting at 0.
	 * @param value  On success, filled with value held at index.
	 * @return  1 if value returned, 0 if no value at index.
	 */
	int getValue(IndexType index, EntryType& value) const
	{
		IndexType blockIndex = index / blockLength;
		if (blockIndex < blockCount)
		{
			EntryType *block = blocks[blockIndex];
			if (block)
			{
				IndexType entryIndex = index % blockLength;
				value = block[entryIndex];
				return 1;
			}
		}
		return 0;
	}

	/**
	 * Set a value in the block_array.
	 * @param index  The index of the value to set, starting at 0.
	 * @param value  Value to set at index.
	 * @return  1 if value set, 0 if failed.
	 */
	int setValue(IndexType index, EntryType value)
	{
		IndexType blockIndex = index / blockLength;
		EntryType* block = getOrCreateBlock(blockIndex);
		if (!block)
			return 0;
		IndexType entryIndex = index % blockLength;
		block[entryIndex] = value;
		return 1;
	}

	bool setValues(IndexType minIndex, IndexType maxIndex, EntryType value)
	{
		// GRC: can be made faster
		for (IndexType index = minIndex; index <= maxIndex; index++)
		{
			if (!setValue(index, value))
				return false;
		}
		return true;
	}

};

/** stores boolean values as individual bits, with no value equivalent to false */
template <typename IndexType, int intBlockLength = 32>
	class bool_array : private block_array<IndexType, unsigned int, intBlockLength>
{
public:
	void clear()
	{
		block_array<IndexType, unsigned int, intBlockLength>::clear();
	}

	void swap(bool_array& other)
	{
		block_array<IndexType, unsigned int, intBlockLength>::swap(other);
	}

	using block_array<IndexType, unsigned int, intBlockLength>::getValue;
	using block_array<IndexType, unsigned int, intBlockLength>::setValue;
	using block_array<IndexType, unsigned int, intBlockLength>::setValues;

	/** @param oldValue  Returns old value so client can determine if status changed */
	int setBool(IndexType index, bool value, bool& oldValue)
	{
		IndexType intIndex = index >> 5;
		unsigned int intValue = 0;
		int hasValue = getValue(intIndex, intValue);
		if (hasValue || value)
		{
			unsigned int mask = (1 << (index & 0x1F));
			oldValue = (0 != (intValue & mask));
			if (oldValue != value)
			{
				return setValue(intIndex, intValue ^ mask);
			}
		}
		else
		{
			oldValue = false;
		}
		return 1;
	}

	bool getBool(IndexType index) const
	{
		IndexType intIndex = index >> 5;
		unsigned int intValue = 0;
		if (getValue(intIndex, intValue))
		{
			unsigned int mask = (1 << (index & 0x1F));
			return (0 != (intValue & mask));
		}
		return false;
	}

	/**
	 * @param lastTrueIndex  Updated to equal or next lower index with true value.
	 * @return  true if found, false if none.
	 */
	bool updateLastTrueIndex(IndexType& lastTrueIndex)
	{
		// GRC this can be made much more efficient
		while (!getBool(lastTrueIndex))
		{
			--lastTrueIndex;
			if (lastTrueIndex < 0)
				return false;
		}
		return true;
	}

	/** @return  true if values for all indexes in range are true; false otherwise */
	bool isRangeTrue(IndexType minIndex, IndexType maxIndex)
	{
		if (minIndex > maxIndex)
			return false;
		// GRC this can be made much more efficient
		IndexType index = minIndex;
		while (getBool(index))
		{
			if (index == maxIndex)
				return true;
			index++;
		}
		return false;
	}

	/** Sets all entries from index 0..indexCount-1 to true.
	 * @return  true if completely successful, false otherwise */
	bool setAllTrue(IndexType indexCount)
	{
		IndexType intIndexCount = indexCount >> 5;
		// bulk set the flags in lots of 32 bits
		if (!setValues(0, intIndexCount-1, 0xFFFFFFFF))
			return false;
		// individually set remaining bits
		for (IndexType index = intIndexCount*32; index < indexCount; index++)
		{
			bool oldValue;
			if (!setBool(index, true, oldValue))
				return false;
		}
		return true;
	}
};

#endif /* !defined (BLOCK_ARRAY_HPP) */
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: valuenode.hxx,v $
 *
 *  $Revision: 1.27 $
 *
 *  last change: $Author: svesik $ $Date: 2004-04-21 13:23:29 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _CONFIGMGR_TREE_VALUENODE_HXX
#define _CONFIGMGR_TREE_VALUENODE_HXX

#ifndef CONFIGMGR_CONFIGURATION_ATTRIBUTES_HXX_
#include "attributes.hxx"
#endif
#ifndef CFGMGR_ANYPAIR_HXX
#include "anypair.hxx"
#endif

#ifndef _COM_SUN_STAR_UNO_ANY_H_
#include <com/sun/star/uno/Any.h>
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif

#ifndef CONFIGMGR_RTTIMACROS_HXX
#include "rttimacros.hxx"
#endif

#include <string.h>
#ifndef INCLUDED_MEMORY
#include <memory>
#define INCLUDED_MEMORY
#endif

namespace configmgr
{

    namespace css = com::sun::star;
    namespace uno = css::uno;

    class INode;
    class ISubtree;
    class ValueNode;

    using rtl::OUString;

    // helper (tag) class
    namespace treeop { struct NoChildCopy {}; struct DeepChildCopy {}; enum { ALL_LEVELS = -1 }; }
    //==========================================================================
    //= Visitors
    //==========================================================================
    struct NodeAction
    {
        virtual void handle(ValueNode const&) = 0;
        virtual void handle(ISubtree const&) = 0;

        void applyToNode(INode const&);
        void applyToChildren(ISubtree const&);
    protected:
        virtual ~NodeAction() {}
    };

    struct NodeModification
    {
        virtual void handle(ValueNode&) = 0;
        virtual void handle(ISubtree&) = 0;

        void applyToNode(INode&);
        void applyToChildren(ISubtree&);
    protected:
        virtual ~NodeModification() {}
    };

    class INode
    {
        OUString                        m_aName;
        node::Attributes            m_aAttributes;

    protected:
        INode(){}

        void markAsDefault(bool _bDefault = true)
        {
            m_aAttributes.markAsDefault(_bDefault);
        }
    public:
        explicit
        INode(node::Attributes);
        INode(OUString const& aName, node::Attributes);

        virtual ~INode();

        virtual std::auto_ptr<INode> clone() const = 0;
    public:

        const OUString& getName() const { return m_aName; }
        node::Attributes getAttributes() const { return m_aAttributes; }

        bool isDefault()   const { return m_aAttributes.isDefault(); }
        bool isLocalized() const { return m_aAttributes.isLocalized(); }

        void modifyState(node::State _eNewState);
        void modifyAccess(node::Access _aAccessLevel);
        void markMandatory();
        void markRemovable();
        void promoteAccessToDefault();
          void forceReadonlyToFinalized();

            // to be used with caution. If the node is referenced from somewhere else under it's old name,
            // you may have problems with this inconsistence
        void setName(const OUString& _rNewName) { m_aName = _rNewName; }

        virtual ValueNode* asValueNode();
        virtual ValueNode const* asValueNode() const;
        virtual ISubtree* asISubtree();
        virtual ISubtree const* asISubtree() const;

        // double dispatch support
        virtual void dispatch(NodeAction&) const = 0;
        virtual void dispatch(NodeModification&) = 0;

        // "rtti"
        RTTI_BASE(INode);
    };

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

    //==========================================================================
    //= ISubtree
    //==========================================================================
    class ISubtree : public INode
    {
        sal_Int16       m_nLevel;                   /// determines if everything is read
        sal_Int16       m_nDefaultLevels;           /// determines if defaults are read
        OUString        m_sId;
        OUString        m_sTemplateName;            /// path of the template for child instantiation
        OUString        m_sTemplateModule;          /// module of the template for child instantiation

        virtual INode* doGetChild(OUString const& name) const = 0;

    protected:
        ISubtree():m_nLevel(0){}

        ISubtree(ISubtree const& other)
        :INode(other)
        ,m_nLevel(other.m_nLevel)
        ,m_sId() // do not copy ID while cloning !
        ,m_nDefaultLevels(other.m_nDefaultLevels)
        ,m_sTemplateName(other.m_sTemplateName)
        ,m_sTemplateModule(other.m_sTemplateModule)
        {}

    public:
        // Ctor for group trees
        ISubtree(const rtl::OUString& aName, const node::Attributes& _rAttrs)
            :INode(aName, _rAttrs)
            ,m_nLevel(0)
            ,m_nDefaultLevels(0)
            {}

        // Ctor for set trees
        ISubtree(const rtl::OUString& aName,
                 const rtl::OUString& _rTemplateName,
                 const rtl::OUString& _rTemplateModule,
                 const node::Attributes& _rAttrs)
            :INode(aName, _rAttrs)
            ,m_nLevel(0)
            ,m_nDefaultLevels(0)
            ,m_sTemplateName(_rTemplateName)
            ,m_sTemplateModule(_rTemplateModule){}

        INode* getChild(OUString const& name)               { return doGetChild(name); }
        INode const* getChild(OUString const& name) const   { return doGetChild(name); }

        ISubtree* asISubtree();
        ISubtree const* asISubtree() const;

        using INode::markAsDefault;

        sal_Int16 getLevel()         const { return m_nLevel; }
        sal_Int16 getDefaultsLevel() const { return m_nDefaultLevels; }

        void setLevels(sal_Int16 _nLevel,sal_Int16 _nDefaultsLevel);

        bool isSetNode() const { return m_sTemplateName.getLength() != 0; }

        void makeSetNode(OUString const& _sTemplateName, OUString const& _sTemplateModule)
        { m_sTemplateName = _sTemplateName; m_sTemplateModule = _sTemplateModule; }

        OUString const& getElementTemplateName()   const { return m_sTemplateName; }
        OUString const& getElementTemplateModule() const { return m_sTemplateModule; }

        virtual INode* addChild(std::auto_ptr<INode> node) =0;                      // takes ownership
        virtual ::std::auto_ptr<INode> removeChild(rtl::OUString const& name) =0;   // releases ownership

        // Iteration support, stop if (action returns true)
        virtual void forEachChild(NodeAction& anAction) const = 0;
        virtual void forEachChild(NodeModification& anAction) = 0;

        // double dispatch support
        virtual void dispatch(NodeAction& anAction) const { anAction.handle(*this); }
        virtual void dispatch(NodeModification& anAction) { anAction.handle(*this); }

        // "rtti"
        RTTI(ISubtree, INode);
    };

    //==========================================================================
    //= ValueNode
    //==========================================================================
    class ValueNode : public INode
    {
        AnyPair m_aValuePair;
        // uno::Type        m_aType;
        // uno::Any     m_aValue;
        // uno::Any     m_aDefaultValue;

    public:
        //ValueNode(){}

        //explicit ValueNode(node::Attributes _aAttrs):INode(_aAttrs){}

        /*
        ValueNode(OUString const& aName, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair()
        {}
        */
        ValueNode(OUString const& aName,uno::Type const& aType, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(aType)
        {
        }
        ValueNode(OUString const& aName,uno::Any const& anAny, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(anAny, selectMember(_aAttrs.isDefault()))
        {
        }
        ValueNode(OUString const& aName,uno::Any const& anAny,uno::Any const& aDefault, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(anAny, aDefault)
        {
        }

        bool isEmpty()      const {return  m_aValuePair.isEmpty();}
        bool isValid()      const {return !m_aValuePair.isEmpty();}

        bool isNull()       const {return m_aValuePair.isNull();}
        bool hasUsableDefault()   const {return getAttributes().isNullable() || m_aValuePair.hasSecond();}

        uno::Type   getValueType()  const {return m_aValuePair.getValueType();}
        uno::Any    getValue()      const {return m_aValuePair.getValue( selectMember(this->isDefault()) );}
        uno::Any    getUserValue()  const {return m_aValuePair.getFirst();}
        uno::Any    getDefault()    const {return m_aValuePair.getSecond();}

        bool setValueType(uno::Type const& _aType);
        bool setValue(uno::Any const& _aValue);
        void setDefault();

        bool changeDefault(uno::Any const& _aValue);
        void promoteToDefault();

        virtual std::auto_ptr<INode> clone() const;

        ValueNode* asValueNode();
        ValueNode const* asValueNode() const;
        // double dispatch support
        virtual void dispatch(NodeAction& anAction) const { anAction.handle(*this); }
        virtual void dispatch(NodeModification& anAction) { anAction.handle(*this); }

        // "rtti"
        RTTI(ValueNode, INode);
    private:
        static AnyPair::SelectMember selectValue() { return AnyPair::SELECT_FIRST; }
        static AnyPair::SelectMember selectDeflt() { return AnyPair::SELECT_SECOND; }
        static AnyPair::SelectMember selectMember(bool bDeflt)
        { return bDeflt ? AnyPair::SELECT_SECOND : AnyPair::SELECT_FIRST; }
    };
    //==========================================================================

    extern bool isLocalizedValueSet(ISubtree const& _aSubtree);

    //==========================================================================
    //= inlines
    //==========================================================================
    inline void NodeAction::applyToNode(INode const& aNode)
    { aNode.dispatch(*this); }
    inline void NodeAction::applyToChildren(ISubtree const& aSubtree)
    { aSubtree.forEachChild(*this); }

    inline void NodeModification::applyToNode(INode& aNode)
    { aNode.dispatch(*this); }
    inline void NodeModification::applyToChildren(ISubtree& aSubtree)
    { aSubtree.forEachChild(*this); }

} // namespace configmgr

#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.27.86); FILE MERGED 2005/09/05 17:04:43 rt 1.27.86.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: valuenode.hxx,v $
 *
 *  $Revision: 1.28 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 04:02:07 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _CONFIGMGR_TREE_VALUENODE_HXX
#define _CONFIGMGR_TREE_VALUENODE_HXX

#ifndef CONFIGMGR_CONFIGURATION_ATTRIBUTES_HXX_
#include "attributes.hxx"
#endif
#ifndef CFGMGR_ANYPAIR_HXX
#include "anypair.hxx"
#endif

#ifndef _COM_SUN_STAR_UNO_ANY_H_
#include <com/sun/star/uno/Any.h>
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif

#ifndef CONFIGMGR_RTTIMACROS_HXX
#include "rttimacros.hxx"
#endif

#include <string.h>
#ifndef INCLUDED_MEMORY
#include <memory>
#define INCLUDED_MEMORY
#endif

namespace configmgr
{

    namespace css = com::sun::star;
    namespace uno = css::uno;

    class INode;
    class ISubtree;
    class ValueNode;

    using rtl::OUString;

    // helper (tag) class
    namespace treeop { struct NoChildCopy {}; struct DeepChildCopy {}; enum { ALL_LEVELS = -1 }; }
    //==========================================================================
    //= Visitors
    //==========================================================================
    struct NodeAction
    {
        virtual void handle(ValueNode const&) = 0;
        virtual void handle(ISubtree const&) = 0;

        void applyToNode(INode const&);
        void applyToChildren(ISubtree const&);
    protected:
        virtual ~NodeAction() {}
    };

    struct NodeModification
    {
        virtual void handle(ValueNode&) = 0;
        virtual void handle(ISubtree&) = 0;

        void applyToNode(INode&);
        void applyToChildren(ISubtree&);
    protected:
        virtual ~NodeModification() {}
    };

    class INode
    {
        OUString                        m_aName;
        node::Attributes            m_aAttributes;

    protected:
        INode(){}

        void markAsDefault(bool _bDefault = true)
        {
            m_aAttributes.markAsDefault(_bDefault);
        }
    public:
        explicit
        INode(node::Attributes);
        INode(OUString const& aName, node::Attributes);

        virtual ~INode();

        virtual std::auto_ptr<INode> clone() const = 0;
    public:

        const OUString& getName() const { return m_aName; }
        node::Attributes getAttributes() const { return m_aAttributes; }

        bool isDefault()   const { return m_aAttributes.isDefault(); }
        bool isLocalized() const { return m_aAttributes.isLocalized(); }

        void modifyState(node::State _eNewState);
        void modifyAccess(node::Access _aAccessLevel);
        void markMandatory();
        void markRemovable();
        void promoteAccessToDefault();
          void forceReadonlyToFinalized();

            // to be used with caution. If the node is referenced from somewhere else under it's old name,
            // you may have problems with this inconsistence
        void setName(const OUString& _rNewName) { m_aName = _rNewName; }

        virtual ValueNode* asValueNode();
        virtual ValueNode const* asValueNode() const;
        virtual ISubtree* asISubtree();
        virtual ISubtree const* asISubtree() const;

        // double dispatch support
        virtual void dispatch(NodeAction&) const = 0;
        virtual void dispatch(NodeModification&) = 0;

        // "rtti"
        RTTI_BASE(INode);
    };

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

    //==========================================================================
    //= ISubtree
    //==========================================================================
    class ISubtree : public INode
    {
        sal_Int16       m_nLevel;                   /// determines if everything is read
        sal_Int16       m_nDefaultLevels;           /// determines if defaults are read
        OUString        m_sId;
        OUString        m_sTemplateName;            /// path of the template for child instantiation
        OUString        m_sTemplateModule;          /// module of the template for child instantiation

        virtual INode* doGetChild(OUString const& name) const = 0;

    protected:
        ISubtree():m_nLevel(0){}

        ISubtree(ISubtree const& other)
        :INode(other)
        ,m_nLevel(other.m_nLevel)
        ,m_sId() // do not copy ID while cloning !
        ,m_nDefaultLevels(other.m_nDefaultLevels)
        ,m_sTemplateName(other.m_sTemplateName)
        ,m_sTemplateModule(other.m_sTemplateModule)
        {}

    public:
        // Ctor for group trees
        ISubtree(const rtl::OUString& aName, const node::Attributes& _rAttrs)
            :INode(aName, _rAttrs)
            ,m_nLevel(0)
            ,m_nDefaultLevels(0)
            {}

        // Ctor for set trees
        ISubtree(const rtl::OUString& aName,
                 const rtl::OUString& _rTemplateName,
                 const rtl::OUString& _rTemplateModule,
                 const node::Attributes& _rAttrs)
            :INode(aName, _rAttrs)
            ,m_nLevel(0)
            ,m_nDefaultLevels(0)
            ,m_sTemplateName(_rTemplateName)
            ,m_sTemplateModule(_rTemplateModule){}

        INode* getChild(OUString const& name)               { return doGetChild(name); }
        INode const* getChild(OUString const& name) const   { return doGetChild(name); }

        ISubtree* asISubtree();
        ISubtree const* asISubtree() const;

        using INode::markAsDefault;

        sal_Int16 getLevel()         const { return m_nLevel; }
        sal_Int16 getDefaultsLevel() const { return m_nDefaultLevels; }

        void setLevels(sal_Int16 _nLevel,sal_Int16 _nDefaultsLevel);

        bool isSetNode() const { return m_sTemplateName.getLength() != 0; }

        void makeSetNode(OUString const& _sTemplateName, OUString const& _sTemplateModule)
        { m_sTemplateName = _sTemplateName; m_sTemplateModule = _sTemplateModule; }

        OUString const& getElementTemplateName()   const { return m_sTemplateName; }
        OUString const& getElementTemplateModule() const { return m_sTemplateModule; }

        virtual INode* addChild(std::auto_ptr<INode> node) =0;                      // takes ownership
        virtual ::std::auto_ptr<INode> removeChild(rtl::OUString const& name) =0;   // releases ownership

        // Iteration support, stop if (action returns true)
        virtual void forEachChild(NodeAction& anAction) const = 0;
        virtual void forEachChild(NodeModification& anAction) = 0;

        // double dispatch support
        virtual void dispatch(NodeAction& anAction) const { anAction.handle(*this); }
        virtual void dispatch(NodeModification& anAction) { anAction.handle(*this); }

        // "rtti"
        RTTI(ISubtree, INode);
    };

    //==========================================================================
    //= ValueNode
    //==========================================================================
    class ValueNode : public INode
    {
        AnyPair m_aValuePair;
        // uno::Type        m_aType;
        // uno::Any     m_aValue;
        // uno::Any     m_aDefaultValue;

    public:
        //ValueNode(){}

        //explicit ValueNode(node::Attributes _aAttrs):INode(_aAttrs){}

        /*
        ValueNode(OUString const& aName, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair()
        {}
        */
        ValueNode(OUString const& aName,uno::Type const& aType, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(aType)
        {
        }
        ValueNode(OUString const& aName,uno::Any const& anAny, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(anAny, selectMember(_aAttrs.isDefault()))
        {
        }
        ValueNode(OUString const& aName,uno::Any const& anAny,uno::Any const& aDefault, node::Attributes _aAttrs)
        : INode(aName, _aAttrs)
        , m_aValuePair(anAny, aDefault)
        {
        }

        bool isEmpty()      const {return  m_aValuePair.isEmpty();}
        bool isValid()      const {return !m_aValuePair.isEmpty();}

        bool isNull()       const {return m_aValuePair.isNull();}
        bool hasUsableDefault()   const {return getAttributes().isNullable() || m_aValuePair.hasSecond();}

        uno::Type   getValueType()  const {return m_aValuePair.getValueType();}
        uno::Any    getValue()      const {return m_aValuePair.getValue( selectMember(this->isDefault()) );}
        uno::Any    getUserValue()  const {return m_aValuePair.getFirst();}
        uno::Any    getDefault()    const {return m_aValuePair.getSecond();}

        bool setValueType(uno::Type const& _aType);
        bool setValue(uno::Any const& _aValue);
        void setDefault();

        bool changeDefault(uno::Any const& _aValue);
        void promoteToDefault();

        virtual std::auto_ptr<INode> clone() const;

        ValueNode* asValueNode();
        ValueNode const* asValueNode() const;
        // double dispatch support
        virtual void dispatch(NodeAction& anAction) const { anAction.handle(*this); }
        virtual void dispatch(NodeModification& anAction) { anAction.handle(*this); }

        // "rtti"
        RTTI(ValueNode, INode);
    private:
        static AnyPair::SelectMember selectValue() { return AnyPair::SELECT_FIRST; }
        static AnyPair::SelectMember selectDeflt() { return AnyPair::SELECT_SECOND; }
        static AnyPair::SelectMember selectMember(bool bDeflt)
        { return bDeflt ? AnyPair::SELECT_SECOND : AnyPair::SELECT_FIRST; }
    };
    //==========================================================================

    extern bool isLocalizedValueSet(ISubtree const& _aSubtree);

    //==========================================================================
    //= inlines
    //==========================================================================
    inline void NodeAction::applyToNode(INode const& aNode)
    { aNode.dispatch(*this); }
    inline void NodeAction::applyToChildren(ISubtree const& aSubtree)
    { aSubtree.forEachChild(*this); }

    inline void NodeModification::applyToNode(INode& aNode)
    { aNode.dispatch(*this); }
    inline void NodeModification::applyToChildren(ISubtree& aSubtree)
    { aSubtree.forEachChild(*this); }

} // namespace configmgr

#endif
<|endoftext|>


<commit_before>#include "buffer_cache/mock.hpp"
#include "arch/arch.hpp"
#include "arch/random_delay.hpp"

/* Internal buf object */

struct internal_buf_t {
    mock_cache_t *cache;
    block_id_t block_id;
    repli_timestamp subtree_recency;
    void *data;
    rwi_lock_t lock;

    internal_buf_t(mock_cache_t *_cache, block_id_t _block_id, repli_timestamp _subtree_recency)
        : cache(_cache), block_id(_block_id), subtree_recency(_subtree_recency),
          data(cache->serializer->malloc()) {
        rassert(data);
        bzero(data, cache->block_size.value());
    }

    ~internal_buf_t() {
        cache->serializer->free(data);
    }

    void destroy() {
        rassert(!lock.locked());

        rassert(cache->bufs[block_id] == this);
        cache->bufs[block_id] = NULL;

        delete this;
    }
};

/* Buf */

void mock_buf_t::on_lock_available() {
    coro_fifo_acq_t acq;
    if (is_write_mode(access)) {
        acq.enter(&internal_buf->cache->write_operation_random_delay_fifo);
    }
    random_delay(cb, &mock_block_available_callback_t::on_block_available, this);
}

block_id_t mock_buf_t::get_block_id() {
    return internal_buf->block_id;
}

const void *mock_buf_t::get_data_read() {
    return internal_buf->data;
}

void *mock_buf_t::get_data_major_write() {
    rassert(access == rwi_write);
    dirty = true;
    return internal_buf->data;
}

void mock_buf_t::apply_patch(buf_patch_t *patch) {
    rassert(access == rwi_write);

    patch->apply_to_buf((char*)internal_buf->data);
    dirty = true;

    delete patch;
}

patch_counter_t mock_buf_t::get_next_patch_counter() {
    return 0;
}

void mock_buf_t::set_data(void *dest, const void *src, const size_t n) {
    size_t offset = reinterpret_cast<const char *>(dest) - reinterpret_cast<const char *>(internal_buf->data);
    apply_patch(new memcpy_patch_t(internal_buf->block_id, get_next_patch_counter(), offset, reinterpret_cast<const char *>(src), n));
}

void mock_buf_t::move_data(void *dest, const void *src, const size_t n) {
    size_t dest_offset = reinterpret_cast<const char *>(dest) - reinterpret_cast<const char *>(internal_buf->data);
    size_t src_offset = reinterpret_cast<const char *>(src) - reinterpret_cast<const char *>(internal_buf->data);
    apply_patch(new memmove_patch_t(internal_buf->block_id, get_next_patch_counter(), dest_offset, src_offset, n));
}

void mock_buf_t::mark_deleted(UNUSED bool write_null) {
    // write_null is ignored for the mock cache.
    rassert(access == rwi_write);
    deleted = true;
}

void mock_buf_t::touch_recency(repli_timestamp timestamp) {
    rassert(access == rwi_write);
    internal_buf->subtree_recency = timestamp;
}

void mock_buf_t::release() {
    internal_buf->lock.unlock();
    if (deleted) internal_buf->destroy();
    delete this;
}

// TODO: Add notiont of recency_dirty
bool mock_buf_t::is_dirty() {
    return dirty;
}

bool mock_buf_t::is_deleted() {
    return deleted;
}

mock_buf_t::mock_buf_t(internal_buf_t *internal_buf, access_t access)
    : internal_buf(internal_buf), access(access), dirty(false), deleted(false) {
}

/* Transaction */

bool mock_transaction_t::commit(mock_transaction_commit_callback_t *callback) {
    switch (access) {
        case rwi_read_sync:
        case rwi_read:
            delete this;
            return true;
        case rwi_write: {
            coro_fifo_acq_t acq;
            acq.enter(&cache->write_operation_random_delay_fifo);
            if (maybe_random_delay(this, &mock_transaction_t::finish_committing, callback)) {
                acq.leave();
                finish_committing(NULL);
                return true;
            } else {
                return false;
            }
        } break;
        case rwi_read_outdated_ok:
        case rwi_intent:
        case rwi_upgrade:
        default:
            unreachable("Bad access");
    }
}

void mock_transaction_t::finish_committing(mock_transaction_commit_callback_t *cb) {
    if (cb) cb->on_txn_commit(this);
    delete this;
}

mock_buf_t *mock_transaction_t::acquire(block_id_t block_id, access_t mode, mock_block_available_callback_t *callback, UNUSED bool should_load) {
    // should_load is ignored for the mock cache.
    if (mode == rwi_write) rassert(this->access == rwi_write);

    rassert(block_id < cache->bufs.get_size());
    internal_buf_t *internal_buf = cache->bufs[block_id];
    rassert(internal_buf);

    if (!(mode == rwi_read || mode == rwi_read_sync || mode == rwi_read_outdated_ok)) {
        internal_buf->subtree_recency = recency_timestamp;
    }

    mock_buf_t *buf = new mock_buf_t(internal_buf, mode);
    if (internal_buf->lock.lock(mode == rwi_read_outdated_ok ? rwi_read : mode, buf)) {
        coro_fifo_acq_t acq;
        if (is_write_mode(mode)) {
            acq.enter(&cache->write_operation_random_delay_fifo);
        }
        if (maybe_random_delay(callback, &mock_block_available_callback_t::on_block_available, buf)) {
            return buf;
        } else {
            return NULL;
        }
    } else {
        buf->cb = callback;
        return NULL;
    }
}

mock_buf_t *mock_transaction_t::allocate() {
    rassert(this->access == rwi_write);

    block_id_t block_id = cache->bufs.get_size();
    cache->bufs.set_size(block_id + 1);
    internal_buf_t *internal_buf = new internal_buf_t(cache, block_id, recency_timestamp);
    cache->bufs[block_id] = internal_buf;
    bool locked __attribute__((unused)) = internal_buf->lock.lock(rwi_write, NULL);
    rassert(locked);

    mock_buf_t *buf = new mock_buf_t(internal_buf, rwi_write);
    return buf;
}

void mock_transaction_t::get_subtree_recencies(block_id_t *block_ids, size_t num_block_ids, repli_timestamp *recencies_out, get_subtree_recencies_callback_t *cb) {
    for (size_t i = 0; i < num_block_ids; ++i) {
        rassert(block_ids[i] < cache->bufs.get_size());
        internal_buf_t *internal_buf = cache->bufs[block_ids[i]];
        rassert(internal_buf);
        recencies_out[i] = internal_buf->subtree_recency;
    }
    cb->got_subtree_recencies();
}

mock_transaction_t::mock_transaction_t(mock_cache_t *_cache, access_t _access, repli_timestamp _recency_timestamp)
    : cache(_cache), order_token(order_token_t::ignore), access(_access), recency_timestamp(_recency_timestamp) {
    cache->transaction_counter.acquire();
}

mock_transaction_t::~mock_transaction_t() {
    cache->transaction_counter.release();
}

/* Cache */

// TODO: Why do we take a static_config if we don't use it?
// (I.i.r.c. we have a similar situation in the mirrored cache.)

void mock_cache_t::create( translator_serializer_t *serializer, UNUSED mirrored_cache_static_config_t *static_config) {
    on_thread_t switcher(serializer->home_thread());

    void *superblock = serializer->malloc();
    bzero(superblock, serializer->get_block_size().value());
    translator_serializer_t::write_t write = translator_serializer_t::write_t::make(
        SUPERBLOCK_ID, repli_timestamp::invalid, superblock, false, NULL);

    struct : public serializer_t::write_txn_callback_t, public cond_t {
        void on_serializer_write_txn() { pulse(); }
    } cb;
    if (!serializer->do_write(&write, 1, DEFAULT_DISK_ACCOUNT, &cb)) cb.wait();

    serializer->free(superblock);
}

// dynamic_config is unused because this is a mock cache and the
// configuration parameters don't apply.
mock_cache_t::mock_cache_t( translator_serializer_t *serializer, UNUSED mirrored_cache_config_t *dynamic_config)
    : serializer(serializer), block_size(serializer->get_block_size())
{
    on_thread_t switcher(serializer->home_thread());

    struct : public serializer_t::read_callback_t, public drain_semaphore_t {
        void on_serializer_read() { release(); }
    } read_cb;

    block_id_t end_block_id = serializer->max_block_id();
    bufs.set_size(end_block_id, NULL);
    for (block_id_t i = 0; i < end_block_id; i++) {
        if (serializer->block_in_use(i)) {
            internal_buf_t *internal_buf = new internal_buf_t(this, i, serializer->get_recency(i));
            bufs[i] = internal_buf;
            if (!serializer->do_read(i, internal_buf->data, DEFAULT_DISK_ACCOUNT, &read_cb)) read_cb.acquire();
        }
    }

    /* Block until all readers are done */
    read_cb.drain();
}

mock_cache_t::~mock_cache_t() {
    /* Wait for all transactions to complete */
    transaction_counter.drain();

    {
        on_thread_t thread_switcher(serializer->home_thread());

        std::vector<translator_serializer_t::write_t> writes;
        for (block_id_t i = 0; i < bufs.get_size(); i++) {
            writes.push_back(translator_serializer_t::write_t::make(
                i, bufs[i] ? bufs[i]->subtree_recency : repli_timestamp::invalid,
                bufs[i] ? bufs[i]->data : NULL,
                true, NULL));
        }

        struct : public serializer_t::write_txn_callback_t, public cond_t {
            void on_serializer_write_txn() { pulse(); }
        } cb;
        if (!serializer->do_write(writes.data(), writes.size(), DEFAULT_DISK_ACCOUNT, &cb)) cb.wait();
    }

    for (block_id_t i = 0; i < bufs.get_size(); i++) {
        if (bufs[i]) delete bufs[i];
    }
}

block_size_t mock_cache_t::get_block_size() {
    return block_size;
}

mock_transaction_t *mock_cache_t::begin_transaction(access_t access, UNUSED int expected_change_count, repli_timestamp recency_timestamp, mock_transaction_begin_callback_t *callback) {
    mock_transaction_t *txn = new mock_transaction_t(this, access, recency_timestamp);

    switch (access) {
        case rwi_read_sync:
        case rwi_read:
            return txn;
        case rwi_write: {
            coro_fifo_acq_t acq;
            acq.enter(&write_operation_random_delay_fifo);
            if (maybe_random_delay(callback, &mock_transaction_begin_callback_t::on_txn_begin, txn)) {
                return txn;
            } else {
                return NULL;
            }
        } break;
        case rwi_read_outdated_ok:
        case rwi_intent:
        case rwi_upgrade:
        default:
            unreachable("Bad access.");
    }
}

void mock_cache_t::offer_read_ahead_buf(UNUSED block_id_t block_id, void *buf, UNUSED repli_timestamp recency_timestamp) {
    serializer->free(buf);
}

bool mock_cache_t::contains_block(UNUSED block_id_t id) {
    return true;    // TODO (maybe) write a more sensible implementation
}
<commit_msg>Fix ordering guarantee in mock cache's begin_transaction<commit_after>#include "buffer_cache/mock.hpp"
#include "arch/arch.hpp"
#include "arch/random_delay.hpp"

/* Internal buf object */

struct internal_buf_t {
    mock_cache_t *cache;
    block_id_t block_id;
    repli_timestamp subtree_recency;
    void *data;
    rwi_lock_t lock;

    internal_buf_t(mock_cache_t *_cache, block_id_t _block_id, repli_timestamp _subtree_recency)
        : cache(_cache), block_id(_block_id), subtree_recency(_subtree_recency),
          data(cache->serializer->malloc()) {
        rassert(data);
        bzero(data, cache->block_size.value());
    }

    ~internal_buf_t() {
        cache->serializer->free(data);
    }

    void destroy() {
        rassert(!lock.locked());

        rassert(cache->bufs[block_id] == this);
        cache->bufs[block_id] = NULL;

        delete this;
    }
};

/* Buf */

void mock_buf_t::on_lock_available() {
    random_delay(cb, &mock_block_available_callback_t::on_block_available, this);
}

block_id_t mock_buf_t::get_block_id() {
    return internal_buf->block_id;
}

const void *mock_buf_t::get_data_read() {
    return internal_buf->data;
}

void *mock_buf_t::get_data_major_write() {
    rassert(access == rwi_write);
    dirty = true;
    return internal_buf->data;
}

void mock_buf_t::apply_patch(buf_patch_t *patch) {
    rassert(access == rwi_write);

    patch->apply_to_buf((char*)internal_buf->data);
    dirty = true;

    delete patch;
}

patch_counter_t mock_buf_t::get_next_patch_counter() {
    return 0;
}

void mock_buf_t::set_data(void *dest, const void *src, const size_t n) {
    size_t offset = reinterpret_cast<const char *>(dest) - reinterpret_cast<const char *>(internal_buf->data);
    apply_patch(new memcpy_patch_t(internal_buf->block_id, get_next_patch_counter(), offset, reinterpret_cast<const char *>(src), n));
}

void mock_buf_t::move_data(void *dest, const void *src, const size_t n) {
    size_t dest_offset = reinterpret_cast<const char *>(dest) - reinterpret_cast<const char *>(internal_buf->data);
    size_t src_offset = reinterpret_cast<const char *>(src) - reinterpret_cast<const char *>(internal_buf->data);
    apply_patch(new memmove_patch_t(internal_buf->block_id, get_next_patch_counter(), dest_offset, src_offset, n));
}

void mock_buf_t::mark_deleted(UNUSED bool write_null) {
    // write_null is ignored for the mock cache.
    rassert(access == rwi_write);
    deleted = true;
}

void mock_buf_t::touch_recency(repli_timestamp timestamp) {
    rassert(access == rwi_write);
    internal_buf->subtree_recency = timestamp;
}

void mock_buf_t::release() {
    internal_buf->lock.unlock();
    if (deleted) internal_buf->destroy();
    delete this;
}

// TODO: Add notiont of recency_dirty
bool mock_buf_t::is_dirty() {
    return dirty;
}

bool mock_buf_t::is_deleted() {
    return deleted;
}

mock_buf_t::mock_buf_t(internal_buf_t *internal_buf, access_t access)
    : internal_buf(internal_buf), access(access), dirty(false), deleted(false) {
}

/* Transaction */

bool mock_transaction_t::commit(mock_transaction_commit_callback_t *callback) {
    switch (access) {
        case rwi_read_sync:
        case rwi_read:
            delete this;
            return true;
        case rwi_write: {
            coro_fifo_acq_t acq;
            acq.enter(&cache->write_operation_random_delay_fifo);
            if (maybe_random_delay(this, &mock_transaction_t::finish_committing, callback)) {
                acq.leave();
                finish_committing(NULL);
                return true;
            } else {
                return false;
            }
        } break;
        case rwi_read_outdated_ok:
        case rwi_intent:
        case rwi_upgrade:
        default:
            unreachable("Bad access");
    }
}

void mock_transaction_t::finish_committing(mock_transaction_commit_callback_t *cb) {
    if (cb) cb->on_txn_commit(this);
    delete this;
}

mock_buf_t *mock_transaction_t::acquire(block_id_t block_id, access_t mode, mock_block_available_callback_t *callback, UNUSED bool should_load) {
    // should_load is ignored for the mock cache.
    if (mode == rwi_write) rassert(this->access == rwi_write);

    rassert(block_id < cache->bufs.get_size());
    internal_buf_t *internal_buf = cache->bufs[block_id];
    rassert(internal_buf);

    if (!(mode == rwi_read || mode == rwi_read_sync || mode == rwi_read_outdated_ok)) {
        internal_buf->subtree_recency = recency_timestamp;
    }

    mock_buf_t *buf = new mock_buf_t(internal_buf, mode);
    if (internal_buf->lock.lock(mode == rwi_read_outdated_ok ? rwi_read : mode, buf)) {
        coro_fifo_acq_t acq;
        if (is_write_mode(mode)) {
            acq.enter(&cache->write_operation_random_delay_fifo);
        }
        if (maybe_random_delay(callback, &mock_block_available_callback_t::on_block_available, buf)) {
            return buf;
        } else {
            return NULL;
        }
    } else {
        buf->cb = callback;
        return NULL;
    }
}

mock_buf_t *mock_transaction_t::allocate() {
    rassert(this->access == rwi_write);

    block_id_t block_id = cache->bufs.get_size();
    cache->bufs.set_size(block_id + 1);
    internal_buf_t *internal_buf = new internal_buf_t(cache, block_id, recency_timestamp);
    cache->bufs[block_id] = internal_buf;
    bool locked __attribute__((unused)) = internal_buf->lock.lock(rwi_write, NULL);
    rassert(locked);

    mock_buf_t *buf = new mock_buf_t(internal_buf, rwi_write);
    return buf;
}

void mock_transaction_t::get_subtree_recencies(block_id_t *block_ids, size_t num_block_ids, repli_timestamp *recencies_out, get_subtree_recencies_callback_t *cb) {
    for (size_t i = 0; i < num_block_ids; ++i) {
        rassert(block_ids[i] < cache->bufs.get_size());
        internal_buf_t *internal_buf = cache->bufs[block_ids[i]];
        rassert(internal_buf);
        recencies_out[i] = internal_buf->subtree_recency;
    }
    cb->got_subtree_recencies();
}

mock_transaction_t::mock_transaction_t(mock_cache_t *_cache, access_t _access, repli_timestamp _recency_timestamp)
    : cache(_cache), order_token(order_token_t::ignore), access(_access), recency_timestamp(_recency_timestamp) {
    cache->transaction_counter.acquire();
}

mock_transaction_t::~mock_transaction_t() {
    cache->transaction_counter.release();
}

/* Cache */

// TODO: Why do we take a static_config if we don't use it?
// (I.i.r.c. we have a similar situation in the mirrored cache.)

void mock_cache_t::create( translator_serializer_t *serializer, UNUSED mirrored_cache_static_config_t *static_config) {
    on_thread_t switcher(serializer->home_thread());

    void *superblock = serializer->malloc();
    bzero(superblock, serializer->get_block_size().value());
    translator_serializer_t::write_t write = translator_serializer_t::write_t::make(
        SUPERBLOCK_ID, repli_timestamp::invalid, superblock, false, NULL);

    struct : public serializer_t::write_txn_callback_t, public cond_t {
        void on_serializer_write_txn() { pulse(); }
    } cb;
    if (!serializer->do_write(&write, 1, DEFAULT_DISK_ACCOUNT, &cb)) cb.wait();

    serializer->free(superblock);
}

// dynamic_config is unused because this is a mock cache and the
// configuration parameters don't apply.
mock_cache_t::mock_cache_t( translator_serializer_t *serializer, UNUSED mirrored_cache_config_t *dynamic_config)
    : serializer(serializer), block_size(serializer->get_block_size())
{
    on_thread_t switcher(serializer->home_thread());

    struct : public serializer_t::read_callback_t, public drain_semaphore_t {
        void on_serializer_read() { release(); }
    } read_cb;

    block_id_t end_block_id = serializer->max_block_id();
    bufs.set_size(end_block_id, NULL);
    for (block_id_t i = 0; i < end_block_id; i++) {
        if (serializer->block_in_use(i)) {
            internal_buf_t *internal_buf = new internal_buf_t(this, i, serializer->get_recency(i));
            bufs[i] = internal_buf;
            if (!serializer->do_read(i, internal_buf->data, DEFAULT_DISK_ACCOUNT, &read_cb)) read_cb.acquire();
        }
    }

    /* Block until all readers are done */
    read_cb.drain();
}

mock_cache_t::~mock_cache_t() {
    /* Wait for all transactions to complete */
    transaction_counter.drain();

    {
        on_thread_t thread_switcher(serializer->home_thread());

        std::vector<translator_serializer_t::write_t> writes;
        for (block_id_t i = 0; i < bufs.get_size(); i++) {
            writes.push_back(translator_serializer_t::write_t::make(
                i, bufs[i] ? bufs[i]->subtree_recency : repli_timestamp::invalid,
                bufs[i] ? bufs[i]->data : NULL,
                true, NULL));
        }

        struct : public serializer_t::write_txn_callback_t, public cond_t {
            void on_serializer_write_txn() { pulse(); }
        } cb;
        if (!serializer->do_write(writes.data(), writes.size(), DEFAULT_DISK_ACCOUNT, &cb)) cb.wait();
    }

    for (block_id_t i = 0; i < bufs.get_size(); i++) {
        if (bufs[i]) delete bufs[i];
    }
}

block_size_t mock_cache_t::get_block_size() {
    return block_size;
}

struct delay_on_txn_begin_wrapper_t {
    mock_transaction_begin_callback_t *callback;
    coro_fifo_acq_t acq;
    void on_txn_begin_wrapper(mock_transaction_t *txn) {
        if (!coro_t::self()) {
            coro_t::spawn(boost::bind(&delay_on_txn_begin_wrapper_t::on_txn_begin_wrapper, this, txn));
            return;
        }
        acq.leave();
        callback->on_txn_begin(txn);
        delete this;
    }
};

mock_transaction_t *mock_cache_t::begin_transaction(access_t access, UNUSED int expected_change_count, repli_timestamp recency_timestamp, mock_transaction_begin_callback_t *callback) {
    mock_transaction_t *txn = new mock_transaction_t(this, access, recency_timestamp);

    switch (access) {
        case rwi_read_sync:
        case rwi_read:
            return txn;
        case rwi_write: {
            delay_on_txn_begin_wrapper_t *delay_on_txn_begin_wrapper = new delay_on_txn_begin_wrapper_t();
            delay_on_txn_begin_wrapper->acq.enter(&write_operation_random_delay_fifo);

            if (maybe_random_delay(delay_on_txn_begin_wrapper, &delay_on_txn_begin_wrapper_t::on_txn_begin_wrapper, txn)) {
                delete delay_on_txn_begin_wrapper;
                return txn;
            } else {
                delay_on_txn_begin_wrapper->callback = callback;
                return NULL;
            }
        } break;
        case rwi_read_outdated_ok:
        case rwi_intent:
        case rwi_upgrade:
        default:
            unreachable("Bad access.");
    }
}

void mock_cache_t::offer_read_ahead_buf(UNUSED block_id_t block_id, void *buf, UNUSED repli_timestamp recency_timestamp) {
    serializer->free(buf);
}

bool mock_cache_t::contains_block(UNUSED block_id_t id) {
    return true;    // TODO (maybe) write a more sensible implementation
}
<|endoftext|>


<commit_before>
#include <gloperate/gloperate-version.h>
#include <gloperate/plugin/plugin_api.h>

#include "multiframepainter/MultiFramePainter.h"
#include "singleframepainter/SingleFramePainter.h"

GLOPERATE_PLUGIN_LIBRARY

    GLOPERATE_PAINTER_PLUGIN(MultiFramePainter
    , "MultiFramePainter"
    , "Moep"
    , GLOPERATE_AUTHOR_ORGANIZATION
    , "v0.0.0" )

    GLOPERATE_PAINTER_PLUGIN(SingleFramePainter
    , "SingleFramePainter"
    , "Moep"
    , GLOPERATE_AUTHOR_ORGANIZATION
    , "v0.0.0" )

GLOPERATE_PLUGIN_LIBRARY_END
<commit_msg>fix version<commit_after>
#include <multiframesampling/multiframesampling-version.h>

#include <gloperate/plugin/plugin_api.h>

#include "multiframepainter/MultiFramePainter.h"
#include "singleframepainter/SingleFramePainter.h"

GLOPERATE_PLUGIN_LIBRARY

    GLOPERATE_PAINTER_PLUGIN(MultiFramePainter
    , "MultiFramePainter"
    , "Moep"
    , MULTIFRAMESAMPLING_AUTHOR_ORGANIZATION
    , "v0.0.0" )

    GLOPERATE_PAINTER_PLUGIN(SingleFramePainter
    , "SingleFramePainter"
    , "Moep"
    , MULTIFRAMESAMPLING_AUTHOR_ORGANIZATION
    , "v0.0.0" )

GLOPERATE_PLUGIN_LIBRARY_END
<|endoftext|>


<commit_before>/**
 * \file
 * \brief Scheduler class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-19
 */

#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/SoftwareTimer.hpp"
#include "distortos/scheduler/MainThreadControlBlock.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"
#include "distortos/architecture/InterruptUnmaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Scheduler::Scheduler() :
		currentThreadControlBlock_{},
		mutexControlBlockListAllocatorPool_{},
		threadControlBlockListAllocatorPool_{},
		threadControlBlockListAllocator_{threadControlBlockListAllocatorPool_},
		runnableList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Runnable},
		suspendedList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Suspended},
		softwareTimerControlBlockSupervisor_{},
		contextSwitchCount_{},
		tickCount_{}
{

}

void Scheduler::add(ThreadControlBlock& threadControlBlock)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	threadControlBlockListAllocatorPool_.feed(threadControlBlock.getLink());
	runnableList_.sortedEmplace(threadControlBlock);
	maybeRequestContextSwitch();
}

void Scheduler::block(ThreadControlBlockList& container)
{
	block(container, currentThreadControlBlock_);
}

int Scheduler::block(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;

		const auto ret = blockInternal(container, iterator);
		if (ret != 0)
			return ret;

		if (iterator != currentThreadControlBlock_)	// blocked thread is not current thread - no forced switch required
			return 0;
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::blockUntil(ThreadControlBlockList& container, const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	const auto iterator = currentThreadControlBlock_;
	bool timedOut {};
	// This lambda unblocks the thread only if it wasn't already unblocked - this is necessary because double unblock
	// should be avoided (it could mess the order of threads of the same priority). In that case it also marks the
	// "timed out" reason of unblocking.
	auto softwareTimer = makeSoftwareTimer(
			[this, iterator, &timedOut]()
			{
				if (iterator->get().getList() != &runnableList_)
				{
					unblockInternal(iterator);
					timedOut = true;
				}
			});
	softwareTimer.start(timePoint);

	block(container);

	return timedOut == false ? 0 : ETIMEDOUT;
}

uint64_t Scheduler::getContextSwitchCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return contextSwitchCount_;
}

uint64_t Scheduler::getTickCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tickCount_;
}

void Scheduler::initialize(MainThreadControlBlock& mainThreadControlBlock)
{
	add(mainThreadControlBlock);
	currentThreadControlBlock_ = runnableList_.begin();
}

void Scheduler::maybeRequestContextSwitch() const
{
	if (isContextSwitchRequired() == true)
		architecture::requestContextSwitch();
}

int Scheduler::remove()
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;
		ThreadControlBlockList terminatedList {threadControlBlockListAllocator_, ThreadControlBlock::State::Terminated};

		const auto ret = blockInternal(terminatedList, currentThreadControlBlock_);
		if (ret != 0)
			return ret;

		terminatedList.begin()->get().terminationHook();
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::resume(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (iterator->get().getList() != &suspendedList_)
		return EINVAL;

	unblock(iterator);
	return 0;
}

void Scheduler::suspend()
{
	suspend(currentThreadControlBlock_);
}

int Scheduler::suspend(const ThreadControlBlockListIterator iterator)
{
	return block(suspendedList_, iterator);
}

void* Scheduler::switchContext(void* const stackPointer)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	++contextSwitchCount_;
	getCurrentThreadControlBlock().getStack().setStackPointer(stackPointer);
	currentThreadControlBlock_ = runnableList_.begin();
	return getCurrentThreadControlBlock().getStack().getStackPointer();
}

bool Scheduler::tickInterruptHandler()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	++tickCount_;

	getCurrentThreadControlBlock().getRoundRobinQuantum().decrement();

	// if the object is on the "runnable" list, it uses SchedulingPolicy::RoundRobin and it used its round-robin
	// quantum, then do the "rotation": move current thread to the end of same-priority group to implement round-robin
	// scheduling
	if (getCurrentThreadControlBlock().getList() == &runnableList_ &&
			getCurrentThreadControlBlock().getSchedulingPolicy() == SchedulingPolicy::RoundRobin &&
			getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		getCurrentThreadControlBlock().getRoundRobinQuantum().reset();
		runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	}

	softwareTimerControlBlockSupervisor_.tickInterruptHandler(TickClock::time_point{TickClock::duration{tickCount_}});

	return isContextSwitchRequired();
}

void Scheduler::unblock(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	unblockInternal(iterator);
	maybeRequestContextSwitch();
}

void Scheduler::yield()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	maybeRequestContextSwitch();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

void Scheduler::addInternal(ThreadControlBlock& threadControlBlock)
{
	threadControlBlockListAllocatorPool_.feed(threadControlBlock.getLink());
	runnableList_.sortedEmplace(threadControlBlock);
}

int Scheduler::blockInternal(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	if (iterator->get().getList() != &runnableList_)
		return EINVAL;

	container.sortedSplice(runnableList_, iterator);

	return 0;
}

void Scheduler::forceContextSwitch() const
{
	architecture::InterruptUnmaskingLock interruptUnmaskingLock;
	architecture::requestContextSwitch();
}

bool Scheduler::isContextSwitchRequired() const
{
	// this check must be first, because during early startup currentThreadControlBlock_ is not yet initialized (so
	// futher conditions would dereference nullptr) and no threads are available
	if (runnableList_.size() <= 1)	// no threads or single thread available?
		return false;				// no context switch possible

	if (getCurrentThreadControlBlock().getList() != &runnableList_)
		return true;

	if (runnableList_.begin() != currentThreadControlBlock_)	// is there a higher-priority thread available?
		return true;

	return false;
}

void Scheduler::unblockInternal(const ThreadControlBlockListIterator iterator)
{
	runnableList_.sortedSplice(*iterator->get().getList(), iterator);
	iterator->get().getRoundRobinQuantum().reset();
}

}	// namespace scheduler

}	// namespace distortos
<commit_msg>Scheduler: implement Scheduler::add() with Scheduler::addInternal()<commit_after>/**
 * \file
 * \brief Scheduler class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-19
 */

#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/SoftwareTimer.hpp"
#include "distortos/scheduler/MainThreadControlBlock.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"
#include "distortos/architecture/InterruptUnmaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Scheduler::Scheduler() :
		currentThreadControlBlock_{},
		mutexControlBlockListAllocatorPool_{},
		threadControlBlockListAllocatorPool_{},
		threadControlBlockListAllocator_{threadControlBlockListAllocatorPool_},
		runnableList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Runnable},
		suspendedList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Suspended},
		softwareTimerControlBlockSupervisor_{},
		contextSwitchCount_{},
		tickCount_{}
{

}

void Scheduler::add(ThreadControlBlock& threadControlBlock)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	addInternal(threadControlBlock);
	maybeRequestContextSwitch();
}

void Scheduler::block(ThreadControlBlockList& container)
{
	block(container, currentThreadControlBlock_);
}

int Scheduler::block(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;

		const auto ret = blockInternal(container, iterator);
		if (ret != 0)
			return ret;

		if (iterator != currentThreadControlBlock_)	// blocked thread is not current thread - no forced switch required
			return 0;
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::blockUntil(ThreadControlBlockList& container, const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	const auto iterator = currentThreadControlBlock_;
	bool timedOut {};
	// This lambda unblocks the thread only if it wasn't already unblocked - this is necessary because double unblock
	// should be avoided (it could mess the order of threads of the same priority). In that case it also marks the
	// "timed out" reason of unblocking.
	auto softwareTimer = makeSoftwareTimer(
			[this, iterator, &timedOut]()
			{
				if (iterator->get().getList() != &runnableList_)
				{
					unblockInternal(iterator);
					timedOut = true;
				}
			});
	softwareTimer.start(timePoint);

	block(container);

	return timedOut == false ? 0 : ETIMEDOUT;
}

uint64_t Scheduler::getContextSwitchCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return contextSwitchCount_;
}

uint64_t Scheduler::getTickCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tickCount_;
}

void Scheduler::initialize(MainThreadControlBlock& mainThreadControlBlock)
{
	add(mainThreadControlBlock);
	currentThreadControlBlock_ = runnableList_.begin();
}

void Scheduler::maybeRequestContextSwitch() const
{
	if (isContextSwitchRequired() == true)
		architecture::requestContextSwitch();
}

int Scheduler::remove()
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;
		ThreadControlBlockList terminatedList {threadControlBlockListAllocator_, ThreadControlBlock::State::Terminated};

		const auto ret = blockInternal(terminatedList, currentThreadControlBlock_);
		if (ret != 0)
			return ret;

		terminatedList.begin()->get().terminationHook();
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::resume(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (iterator->get().getList() != &suspendedList_)
		return EINVAL;

	unblock(iterator);
	return 0;
}

void Scheduler::suspend()
{
	suspend(currentThreadControlBlock_);
}

int Scheduler::suspend(const ThreadControlBlockListIterator iterator)
{
	return block(suspendedList_, iterator);
}

void* Scheduler::switchContext(void* const stackPointer)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	++contextSwitchCount_;
	getCurrentThreadControlBlock().getStack().setStackPointer(stackPointer);
	currentThreadControlBlock_ = runnableList_.begin();
	return getCurrentThreadControlBlock().getStack().getStackPointer();
}

bool Scheduler::tickInterruptHandler()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	++tickCount_;

	getCurrentThreadControlBlock().getRoundRobinQuantum().decrement();

	// if the object is on the "runnable" list, it uses SchedulingPolicy::RoundRobin and it used its round-robin
	// quantum, then do the "rotation": move current thread to the end of same-priority group to implement round-robin
	// scheduling
	if (getCurrentThreadControlBlock().getList() == &runnableList_ &&
			getCurrentThreadControlBlock().getSchedulingPolicy() == SchedulingPolicy::RoundRobin &&
			getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		getCurrentThreadControlBlock().getRoundRobinQuantum().reset();
		runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	}

	softwareTimerControlBlockSupervisor_.tickInterruptHandler(TickClock::time_point{TickClock::duration{tickCount_}});

	return isContextSwitchRequired();
}

void Scheduler::unblock(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	unblockInternal(iterator);
	maybeRequestContextSwitch();
}

void Scheduler::yield()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	maybeRequestContextSwitch();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

void Scheduler::addInternal(ThreadControlBlock& threadControlBlock)
{
	threadControlBlockListAllocatorPool_.feed(threadControlBlock.getLink());
	runnableList_.sortedEmplace(threadControlBlock);
}

int Scheduler::blockInternal(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	if (iterator->get().getList() != &runnableList_)
		return EINVAL;

	container.sortedSplice(runnableList_, iterator);

	return 0;
}

void Scheduler::forceContextSwitch() const
{
	architecture::InterruptUnmaskingLock interruptUnmaskingLock;
	architecture::requestContextSwitch();
}

bool Scheduler::isContextSwitchRequired() const
{
	// this check must be first, because during early startup currentThreadControlBlock_ is not yet initialized (so
	// futher conditions would dereference nullptr) and no threads are available
	if (runnableList_.size() <= 1)	// no threads or single thread available?
		return false;				// no context switch possible

	if (getCurrentThreadControlBlock().getList() != &runnableList_)
		return true;

	if (runnableList_.begin() != currentThreadControlBlock_)	// is there a higher-priority thread available?
		return true;

	return false;
}

void Scheduler::unblockInternal(const ThreadControlBlockListIterator iterator)
{
	runnableList_.sortedSplice(*iterator->get().getList(), iterator);
	iterator->get().getRoundRobinQuantum().reset();
}

}	// namespace scheduler

}	// namespace distortos
<|endoftext|>


<commit_before>/**
 * \file
 * \brief Scheduler class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-04
 */

#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/SoftwareTimer.hpp"
#include "distortos/scheduler/MainThreadControlBlock.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"
#include "distortos/architecture/InterruptUnmaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Scheduler::Scheduler() :
		currentThreadControlBlock_{},
		mutexControlBlockListAllocatorPool_{},
		threadControlBlockListAllocatorPool_{},
		threadControlBlockListAllocator_{threadControlBlockListAllocatorPool_},
		runnableList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Runnable},
		suspendedList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Suspended},
		softwareTimerControlBlockSupervisor_{},
		contextSwitchCount_{},
		tickCount_{}
{

}

void Scheduler::add(ThreadControlBlock& threadControlBlock)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	threadControlBlockListAllocatorPool_.feed(threadControlBlock.getLink());
	runnableList_.sortedEmplace(threadControlBlock);
	maybeRequestContextSwitch();
}

void Scheduler::block(ThreadControlBlockList& container)
{
	block(container, currentThreadControlBlock_);
}

int Scheduler::block(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;

		const auto ret = blockInternal(container, iterator);
		if (ret != 0)
			return ret;

		if (iterator != currentThreadControlBlock_)	// blocked thread is not current thread - no forced switch required
			return 0;
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::blockUntil(ThreadControlBlockList& container, const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	const auto iterator = currentThreadControlBlock_;
	bool timedOut {};
	// This lambda unblocks the thread only if it wasn't already unblocked - this is necessary because double unblock
	// should be avoided (it could mess the order of threads of the same priority). In that case it also marks the
	// "timed out" reason of unblocking.
	auto softwareTimer = makeSoftwareTimer(
			[this, iterator, &timedOut]()
			{
				if (iterator->get().getList() != &runnableList_)
				{
					unblockInternal(iterator);
					timedOut = true;
				}
			});
	softwareTimer.start(timePoint);

	block(container);

	return timedOut == false ? 0 : ETIMEDOUT;
}

uint64_t Scheduler::getTickCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tickCount_;
}

void Scheduler::initialize(MainThreadControlBlock& mainThreadControlBlock)
{
	add(mainThreadControlBlock);
	currentThreadControlBlock_ = runnableList_.begin();
}

void Scheduler::maybeRequestContextSwitch() const
{
	if (isContextSwitchRequired() == true)
		architecture::requestContextSwitch();
}

int Scheduler::remove()
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;
		ThreadControlBlockList terminatedList {threadControlBlockListAllocator_, ThreadControlBlock::State::Terminated};

		const auto ret = blockInternal(terminatedList, currentThreadControlBlock_);
		if (ret != 0)
			return ret;

		terminatedList.begin()->get().terminationHook();
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::resume(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (iterator->get().getList() != &suspendedList_)
		return EINVAL;

	unblock(iterator);
	return 0;
}

void Scheduler::suspend()
{
	suspend(currentThreadControlBlock_);
}

int Scheduler::suspend(const ThreadControlBlockListIterator iterator)
{
	return block(suspendedList_, iterator);
}

void* Scheduler::switchContext(void* const stackPointer)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	getCurrentThreadControlBlock().getStack().setStackPointer(stackPointer);
	currentThreadControlBlock_ = runnableList_.begin();
	return getCurrentThreadControlBlock().getStack().getStackPointer();
}

bool Scheduler::tickInterruptHandler()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	++tickCount_;

	getCurrentThreadControlBlock().getRoundRobinQuantum().decrement();

	// if the object is on the "runnable" list and it used its round-robin quantum, then do the "rotation": move current
	// thread to the end of same-priority group to implement round-robin scheduling
	if (getCurrentThreadControlBlock().getList() == &runnableList_ &&
			getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		getCurrentThreadControlBlock().getRoundRobinQuantum().reset();
		runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	}

	softwareTimerControlBlockSupervisor_.tickInterruptHandler(TickClock::time_point{TickClock::duration{tickCount_}});

	return isContextSwitchRequired();
}

void Scheduler::unblock(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	unblockInternal(iterator);
	maybeRequestContextSwitch();
}

void Scheduler::yield()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	maybeRequestContextSwitch();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int Scheduler::blockInternal(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	if (iterator->get().getList() != &runnableList_)
		return EINVAL;

	container.sortedSplice(runnableList_, iterator);

	return 0;
}

void Scheduler::forceContextSwitch() const
{
	architecture::InterruptUnmaskingLock interruptUnmaskingLock;
	architecture::requestContextSwitch();
}

bool Scheduler::isContextSwitchRequired() const
{
	// this check must be first, because during early startup currentThreadControlBlock_ is not yet initialized (so
	// futher conditions would dereference nullptr) and no threads are available
	if (runnableList_.size() <= 1)	// no threads or single thread available?
		return false;				// no context switch possible

	if (getCurrentThreadControlBlock().getList() != &runnableList_)
		return true;

	if (runnableList_.begin() != currentThreadControlBlock_)	// is there a higher-priority thread available?
		return true;

	if (getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		const auto nextThread = ++runnableList_.begin();
		const auto nextThreadPriority = nextThread->get().getPriority();
		// thread with same priority available?
		if (getCurrentThreadControlBlock().getPriority() == nextThreadPriority)
			return true;	// switch context to do round-robin scheduling
	}

	return false;
}

void Scheduler::unblockInternal(const ThreadControlBlockListIterator iterator)
{
	runnableList_.sortedSplice(*iterator->get().getList(), iterator);
	iterator->get().getRoundRobinQuantum().reset();
}

}	// namespace scheduler

}	// namespace distortos
<commit_msg>Scheduler: count context switches in Scheduler::switchContext()<commit_after>/**
 * \file
 * \brief Scheduler class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-04
 */

#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/SoftwareTimer.hpp"
#include "distortos/scheduler/MainThreadControlBlock.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"
#include "distortos/architecture/InterruptUnmaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Scheduler::Scheduler() :
		currentThreadControlBlock_{},
		mutexControlBlockListAllocatorPool_{},
		threadControlBlockListAllocatorPool_{},
		threadControlBlockListAllocator_{threadControlBlockListAllocatorPool_},
		runnableList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Runnable},
		suspendedList_{threadControlBlockListAllocator_, ThreadControlBlock::State::Suspended},
		softwareTimerControlBlockSupervisor_{},
		contextSwitchCount_{},
		tickCount_{}
{

}

void Scheduler::add(ThreadControlBlock& threadControlBlock)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	threadControlBlockListAllocatorPool_.feed(threadControlBlock.getLink());
	runnableList_.sortedEmplace(threadControlBlock);
	maybeRequestContextSwitch();
}

void Scheduler::block(ThreadControlBlockList& container)
{
	block(container, currentThreadControlBlock_);
}

int Scheduler::block(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;

		const auto ret = blockInternal(container, iterator);
		if (ret != 0)
			return ret;

		if (iterator != currentThreadControlBlock_)	// blocked thread is not current thread - no forced switch required
			return 0;
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::blockUntil(ThreadControlBlockList& container, const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	const auto iterator = currentThreadControlBlock_;
	bool timedOut {};
	// This lambda unblocks the thread only if it wasn't already unblocked - this is necessary because double unblock
	// should be avoided (it could mess the order of threads of the same priority). In that case it also marks the
	// "timed out" reason of unblocking.
	auto softwareTimer = makeSoftwareTimer(
			[this, iterator, &timedOut]()
			{
				if (iterator->get().getList() != &runnableList_)
				{
					unblockInternal(iterator);
					timedOut = true;
				}
			});
	softwareTimer.start(timePoint);

	block(container);

	return timedOut == false ? 0 : ETIMEDOUT;
}

uint64_t Scheduler::getTickCount() const
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tickCount_;
}

void Scheduler::initialize(MainThreadControlBlock& mainThreadControlBlock)
{
	add(mainThreadControlBlock);
	currentThreadControlBlock_ = runnableList_.begin();
}

void Scheduler::maybeRequestContextSwitch() const
{
	if (isContextSwitchRequired() == true)
		architecture::requestContextSwitch();
}

int Scheduler::remove()
{
	{
		architecture::InterruptMaskingLock interruptMaskingLock;
		ThreadControlBlockList terminatedList {threadControlBlockListAllocator_, ThreadControlBlock::State::Terminated};

		const auto ret = blockInternal(terminatedList, currentThreadControlBlock_);
		if (ret != 0)
			return ret;

		terminatedList.begin()->get().terminationHook();
	}

	forceContextSwitch();

	return 0;
}

int Scheduler::resume(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (iterator->get().getList() != &suspendedList_)
		return EINVAL;

	unblock(iterator);
	return 0;
}

void Scheduler::suspend()
{
	suspend(currentThreadControlBlock_);
}

int Scheduler::suspend(const ThreadControlBlockListIterator iterator)
{
	return block(suspendedList_, iterator);
}

void* Scheduler::switchContext(void* const stackPointer)
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	++contextSwitchCount_;
	getCurrentThreadControlBlock().getStack().setStackPointer(stackPointer);
	currentThreadControlBlock_ = runnableList_.begin();
	return getCurrentThreadControlBlock().getStack().getStackPointer();
}

bool Scheduler::tickInterruptHandler()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	++tickCount_;

	getCurrentThreadControlBlock().getRoundRobinQuantum().decrement();

	// if the object is on the "runnable" list and it used its round-robin quantum, then do the "rotation": move current
	// thread to the end of same-priority group to implement round-robin scheduling
	if (getCurrentThreadControlBlock().getList() == &runnableList_ &&
			getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		getCurrentThreadControlBlock().getRoundRobinQuantum().reset();
		runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	}

	softwareTimerControlBlockSupervisor_.tickInterruptHandler(TickClock::time_point{TickClock::duration{tickCount_}});

	return isContextSwitchRequired();
}

void Scheduler::unblock(const ThreadControlBlockListIterator iterator)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	unblockInternal(iterator);
	maybeRequestContextSwitch();
}

void Scheduler::yield()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	runnableList_.sortedSplice(runnableList_, currentThreadControlBlock_);
	maybeRequestContextSwitch();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int Scheduler::blockInternal(ThreadControlBlockList& container, const ThreadControlBlockListIterator iterator)
{
	if (iterator->get().getList() != &runnableList_)
		return EINVAL;

	container.sortedSplice(runnableList_, iterator);

	return 0;
}

void Scheduler::forceContextSwitch() const
{
	architecture::InterruptUnmaskingLock interruptUnmaskingLock;
	architecture::requestContextSwitch();
}

bool Scheduler::isContextSwitchRequired() const
{
	// this check must be first, because during early startup currentThreadControlBlock_ is not yet initialized (so
	// futher conditions would dereference nullptr) and no threads are available
	if (runnableList_.size() <= 1)	// no threads or single thread available?
		return false;				// no context switch possible

	if (getCurrentThreadControlBlock().getList() != &runnableList_)
		return true;

	if (runnableList_.begin() != currentThreadControlBlock_)	// is there a higher-priority thread available?
		return true;

	if (getCurrentThreadControlBlock().getRoundRobinQuantum().isZero() == true)
	{
		const auto nextThread = ++runnableList_.begin();
		const auto nextThreadPriority = nextThread->get().getPriority();
		// thread with same priority available?
		if (getCurrentThreadControlBlock().getPriority() == nextThreadPriority)
			return true;	// switch context to do round-robin scheduling
	}

	return false;
}

void Scheduler::unblockInternal(const ThreadControlBlockListIterator iterator)
{
	runnableList_.sortedSplice(*iterator->get().getList(), iterator);
	iterator->get().getRoundRobinQuantum().reset();
}

}	// namespace scheduler

}	// namespace distortos
<|endoftext|>