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

#pragma once

#include "js_class.hpp"
#include "js_collection.hpp"
#include "js_sync_util.hpp"
#include "js_app_credentials.hpp"
#include "object-store/src/sync/app.hpp"
#include "object-store/src/sync/sync_user.hpp"
#include "platform.hpp"

#include "sync/sync_config.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_session.hpp"
#include "sync/sync_user.hpp"

namespace realm {
namespace js {

using SharedUser = std::shared_ptr<realm::SyncUser>;
using SharedApp = std::shared_ptr<realm::app::App>;

template<typename T>
class User : public SharedUser {
public:
    User(SharedUser user, SharedApp app) : SharedUser(std::move(user)), m_app(std::move(app)) {}
    User(SharedUser user) : SharedUser(std::move(user)), m_app(nullptr) {}
    User(User &&) = default;

    User& operator=(User &&) = default;
    User& operator=(User const&) = default;

    SharedApp m_app;
};

template<typename T>
class UserClass : public ClassDefinition<T, User<T>> {
    using GlobalContextType = typename T::GlobalContext;
    using ContextType = typename T::Context;
    using FunctionType = typename T::Function;
    using ObjectType = typename T::Object;
    using ValueType = typename T::Value;
    using String = js::String<T>;
    using Object = js::Object<T>;
    using Value = js::Value<T>;
    using Function = js::Function<T>;
    using ReturnValue = js::ReturnValue<T>;
    using Arguments = js::Arguments<T>;

public:
    std::string const name = "User";

    static FunctionType create_constructor(ContextType);
    static ObjectType create_instance(ContextType, SharedUser, SharedApp);

    static void get_identity(ContextType, ObjectType, ReturnValue &);
    static void get_token(ContextType, ObjectType, ReturnValue &);
    static void get_profile(ContextType, ObjectType, ReturnValue &);
    static void set_profile(ContextType, ObjectType, ValueType);
    static void is_logged_in(ContextType, ObjectType, ReturnValue &);
    static void get_state(ContextType, ObjectType, ReturnValue &);

    PropertyMap<T> const properties = {
        {"identity", {wrap<get_identity>, nullptr}},
        {"token", {wrap<get_token>, nullptr}},
        {"profile", {wrap<get_profile>, wrap<set_profile>}},
        {"isLoggedIn", {wrap<is_logged_in>, nullptr}},
        {"state", {wrap<get_state>, nullptr}}
    };

    MethodMap<T> const static_methods = {
    };

    static void logout(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void session_for_on_disk_path(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void delete_user(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void link_credentials(ContextType, ObjectType, Arguments&, ReturnValue&);

    MethodMap<T> const methods = {
        {"logOut", wrap<logout>},
        {"_sessionForOnDiskPath", wrap<session_for_on_disk_path>},
        {"_deleteUser", wrap<delete_user>},
        {"_linkCredentials", wrap<link_credentials>}
    };
};

template<typename T>
inline typename T::Function UserClass<T>::create_constructor(ContextType ctx) {
    FunctionType user_constructor = ObjectWrap<T, UserClass<T>>::create_constructor(ctx);
    return user_constructor;
}

template<typename T>
typename T::Object UserClass<T>::create_instance(ContextType ctx, SharedUser user, SharedApp app) {
    return create_object<T, UserClass<T>>(ctx, new User<T>(user, app));
}

template<typename T>
void UserClass<T>::get_identity(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    std::string identity = get_internal<T, UserClass<T>>(object)->get()->identity();
    return_value.set(identity);
}

template<typename T>
void UserClass<T>::get_token(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    std::string token = get_internal<T, UserClass<T>>(object)->get()->refresh_token();
    return_value.set(token);
}

template<typename T>
void UserClass<T>::is_logged_in(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    auto logged_in = get_internal<T, UserClass<T>>(object)->get()->is_logged_in();
    return_value.set(logged_in);
}

template<typename T>
void UserClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    auto state = get_internal<T, UserClass<T>>(object)->get()->state();

    switch (state) {
    case SyncUser::State::LoggedOut:
        return_value.set("LoggedOut");
        break;
    case SyncUser::State::LoggedIn:
        return_value.set("LoggedIn");
        break;
    case SyncUser::State::Removed:
        return_value.set("Removed");
        break;
    }
}

template<typename T>
void UserClass<T>::get_profile(ContextType ctx, ObjectType object, ReturnValue& return_value) {
    static const String string_name        = "name";
    static const String string_email       = "email";
    static const String string_picture_url = "pictureUrl";
    static const String string_first_name  = "firstName";
    static const String string_last_name   = "lastName";
    static const String string_gender      = "gender";
    static const String string_birthday    = "birthday";
    static const String string_min_age     = "minAge";
    static const String string_max_age     = "maxAge";

    auto user_profile = get_internal<T, UserClass<T>>(object)->get()->user_profile();

    auto profile_object = Object::create_empty(ctx);
#define STRING_TO_PROP(propname) \
    util::Optional<std::string> optional_##propname = user_profile.propname; \
    if (optional_##propname ) { \
        Object::set_property(ctx, profile_object, string_##propname, Value::from_string(ctx, *optional_##propname)); \
    }

    STRING_TO_PROP(name)
    STRING_TO_PROP(email)
    STRING_TO_PROP(picture_url)
    STRING_TO_PROP(first_name)
    STRING_TO_PROP(last_name)
    STRING_TO_PROP(gender)
    STRING_TO_PROP(birthday)
    STRING_TO_PROP(min_age)
    STRING_TO_PROP(max_age)
#undef STRING_TO_PROP

    return_value.set(profile_object);
}

template<typename T>
void UserClass<T>::logout(ContextType, ObjectType this_object, Arguments& args, ReturnValue &) {
    args.validate_count(0);
    get_internal<T, UserClass<T>>(this_object)->get()->log_out();
}

template<typename T>
void UserClass<T>::delete_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) {
    args.validate_count(1);

    auto user = get_internal<T, UserClass<T>>(this_object);
    auto callback = Value::validated_to_function(ctx, args[0], "callback");

    Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
    Protected<FunctionType> protected_callback(ctx, callback);
    Protected<ObjectType> protected_this(ctx, this_object);

    auto callback_handler([=](util::Optional<app::AppError> error) {
        HANDLESCOPE
        ObjectType error_object = Object::create_empty(protected_ctx);
        if (error) {
            Object::set_property(protected_ctx, error_object, "message", Value::from_string(protected_ctx, error->message));
            Object::set_property(protected_ctx, error_object, "code", Value::from_number(protected_ctx, error->error_code.value()));
        }

        ValueType callback_arguments[1];
        callback_arguments[0] = error_object;
        Function::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments);
    });

    user->m_app->remove_user(*user, callback_handler);
}

template<typename T>
void UserClass<T>::link_credentials(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue &) {
    args.validate_count(2);
    auto user = get_internal<T, UserClass<T>>(this_object);

    auto credentials = *get_internal<T, CredentialsClass<T>>(Value::validated_to_object(ctx, args[0], "credentials"));
    auto callback = Value::validated_to_function(ctx, args[1], "callback");

    Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
    Protected<FunctionType> protected_callback(ctx, callback);
    Protected<ObjectType> protected_this(ctx, this_object);

    auto callback_handler([=](SharedUser shared_user, util::Optional<app::AppError> error) {
        HANDLESCOPE

        if (error) {
            ObjectType error_object = Object::create_empty(protected_ctx);
            Object::set_property(protected_ctx, error_object, "message", Value::from_string(protected_ctx, error->message));
            Object::set_property(protected_ctx, error_object, "code", Value::from_number(protected_ctx, error->error_code.value()));

            ValueType callback_arguments[2];
            callback_arguments[0] = Value::from_undefined(protected_ctx);
            callback_arguments[1] = error_object;
            Function::callback(protected_ctx, protected_callback, protected_this, 2, callback_arguments);
            return;
        }

        ValueType callback_arguments[2];
        callback_arguments[0] = create_object<T, UserClass<T>>(ctx, new User<T>(shared_user, user->m_app));
        callback_arguments[1] = Value::from_undefined(protected_ctx);
        Function::callback(protected_ctx, protected_callback, typename T::Object(), 2, callback_arguments);
    });

    user->m_app->link_user(*user, credentials, callback_handler);
}

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

#pragma once

#include "js_class.hpp"
#include "js_collection.hpp"
#include "js_sync_util.hpp"
#include "js_app_credentials.hpp"
#include "object-store/src/sync/app.hpp"
#include "object-store/src/sync/sync_user.hpp"
#include "platform.hpp"

#include "sync/sync_config.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_session.hpp"
#include "sync/sync_user.hpp"

namespace realm {
namespace js {

using SharedUser = std::shared_ptr<realm::SyncUser>;
using SharedApp = std::shared_ptr<realm::app::App>;

template<typename T>
class User : public SharedUser {
public:
    User(SharedUser user, SharedApp app) : SharedUser(std::move(user)), m_app(std::move(app)) {}
    User(SharedUser user) : SharedUser(std::move(user)), m_app(nullptr) {}
    User(User &&) = default;

    User& operator=(User &&) = default;
    User& operator=(User const&) = default;

    SharedApp m_app;
};

template<typename T>
class UserClass : public ClassDefinition<T, User<T>> {
    using GlobalContextType = typename T::GlobalContext;
    using ContextType = typename T::Context;
    using FunctionType = typename T::Function;
    using ObjectType = typename T::Object;
    using ValueType = typename T::Value;
    using String = js::String<T>;
    using Object = js::Object<T>;
    using Value = js::Value<T>;
    using Function = js::Function<T>;
    using ReturnValue = js::ReturnValue<T>;
    using Arguments = js::Arguments<T>;

public:
    std::string const name = "User";

    static FunctionType create_constructor(ContextType);
    static ObjectType create_instance(ContextType, SharedUser, SharedApp);

    static void get_identity(ContextType, ObjectType, ReturnValue &);
    static void get_token(ContextType, ObjectType, ReturnValue &);
    static void get_profile(ContextType, ObjectType, ReturnValue &);
    static void is_logged_in(ContextType, ObjectType, ReturnValue &);
    static void get_state(ContextType, ObjectType, ReturnValue &);

    PropertyMap<T> const properties = {
        {"identity", {wrap<get_identity>, nullptr}},
        {"token", {wrap<get_token>, nullptr}},
        {"profile", {wrap<get_profile>, nullptr}},
        {"isLoggedIn", {wrap<is_logged_in>, nullptr}},
        {"state", {wrap<get_state>, nullptr}}
    };

    MethodMap<T> const static_methods = {
    };

    static void logout(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void session_for_on_disk_path(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void delete_user(ContextType, ObjectType, Arguments&, ReturnValue&);
    static void link_credentials(ContextType, ObjectType, Arguments&, ReturnValue&);

    MethodMap<T> const methods = {
        {"logOut", wrap<logout>},
        {"_sessionForOnDiskPath", wrap<session_for_on_disk_path>},
        {"_deleteUser", wrap<delete_user>},
        {"_linkCredentials", wrap<link_credentials>}
    };
};

template<typename T>
inline typename T::Function UserClass<T>::create_constructor(ContextType ctx) {
    FunctionType user_constructor = ObjectWrap<T, UserClass<T>>::create_constructor(ctx);
    return user_constructor;
}

template<typename T>
typename T::Object UserClass<T>::create_instance(ContextType ctx, SharedUser user, SharedApp app) {
    return create_object<T, UserClass<T>>(ctx, new User<T>(user, app));
}

template<typename T>
void UserClass<T>::get_identity(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    std::string identity = get_internal<T, UserClass<T>>(object)->get()->identity();
    return_value.set(identity);
}

template<typename T>
void UserClass<T>::get_token(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    std::string token = get_internal<T, UserClass<T>>(object)->get()->refresh_token();
    return_value.set(token);
}

template<typename T>
void UserClass<T>::is_logged_in(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    auto logged_in = get_internal<T, UserClass<T>>(object)->get()->is_logged_in();
    return_value.set(logged_in);
}

template<typename T>
void UserClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue &return_value) {
    auto state = get_internal<T, UserClass<T>>(object)->get()->state();

    switch (state) {
    case SyncUser::State::LoggedOut:
        return_value.set("LoggedOut");
        break;
    case SyncUser::State::LoggedIn:
        return_value.set("LoggedIn");
        break;
    case SyncUser::State::Removed:
        return_value.set("Removed");
        break;
    }
}

template<typename T>
void UserClass<T>::get_profile(ContextType ctx, ObjectType object, ReturnValue& return_value) {
    static const String string_name        = "name";
    static const String string_email       = "email";
    static const String string_picture_url = "pictureUrl";
    static const String string_first_name  = "firstName";
    static const String string_last_name   = "lastName";
    static const String string_gender      = "gender";
    static const String string_birthday    = "birthday";
    static const String string_min_age     = "minAge";
    static const String string_max_age     = "maxAge";

    auto user_profile = get_internal<T, UserClass<T>>(object)->get()->user_profile();

    auto profile_object = Object::create_empty(ctx);
#define STRING_TO_PROP(propname) \
    util::Optional<std::string> optional_##propname = user_profile.propname; \
    if (optional_##propname ) { \
        Object::set_property(ctx, profile_object, string_##propname, Value::from_string(ctx, *optional_##propname)); \
    }

    STRING_TO_PROP(name)
    STRING_TO_PROP(email)
    STRING_TO_PROP(picture_url)
    STRING_TO_PROP(first_name)
    STRING_TO_PROP(last_name)
    STRING_TO_PROP(gender)
    STRING_TO_PROP(birthday)
    STRING_TO_PROP(min_age)
    STRING_TO_PROP(max_age)
#undef STRING_TO_PROP

    return_value.set(profile_object);
}

template<typename T>
void UserClass<T>::logout(ContextType, ObjectType this_object, Arguments& args, ReturnValue &) {
    args.validate_count(0);
    get_internal<T, UserClass<T>>(this_object)->get()->log_out();
}

template<typename T>
void UserClass<T>::delete_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) {
    args.validate_count(1);

    auto user = get_internal<T, UserClass<T>>(this_object);
    auto callback = Value::validated_to_function(ctx, args[0], "callback");

    Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
    Protected<FunctionType> protected_callback(ctx, callback);
    Protected<ObjectType> protected_this(ctx, this_object);

    auto callback_handler([=](util::Optional<app::AppError> error) {
        HANDLESCOPE
        ObjectType error_object = Object::create_empty(protected_ctx);
        if (error) {
            Object::set_property(protected_ctx, error_object, "message", Value::from_string(protected_ctx, error->message));
            Object::set_property(protected_ctx, error_object, "code", Value::from_number(protected_ctx, error->error_code.value()));
        }

        ValueType callback_arguments[1];
        callback_arguments[0] = error_object;
        Function::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments);
    });

    user->m_app->remove_user(*user, callback_handler);
}

template<typename T>
void UserClass<T>::link_credentials(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue &) {
    args.validate_count(2);
    auto user = get_internal<T, UserClass<T>>(this_object);

    auto credentials = *get_internal<T, CredentialsClass<T>>(Value::validated_to_object(ctx, args[0], "credentials"));
    auto callback = Value::validated_to_function(ctx, args[1], "callback");

    Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
    Protected<FunctionType> protected_callback(ctx, callback);
    Protected<ObjectType> protected_this(ctx, this_object);

    auto callback_handler([=](SharedUser shared_user, util::Optional<app::AppError> error) {
        HANDLESCOPE

        if (error) {
            ObjectType error_object = Object::create_empty(protected_ctx);
            Object::set_property(protected_ctx, error_object, "message", Value::from_string(protected_ctx, error->message));
            Object::set_property(protected_ctx, error_object, "code", Value::from_number(protected_ctx, error->error_code.value()));

            ValueType callback_arguments[2];
            callback_arguments[0] = Value::from_undefined(protected_ctx);
            callback_arguments[1] = error_object;
            Function::callback(protected_ctx, protected_callback, protected_this, 2, callback_arguments);
            return;
        }

        ValueType callback_arguments[2];
        callback_arguments[0] = create_object<T, UserClass<T>>(ctx, new User<T>(shared_user, user->m_app));
        callback_arguments[1] = Value::from_undefined(protected_ctx);
        Function::callback(protected_ctx, protected_callback, typename T::Object(), 2, callback_arguments);
    });

    user->m_app->link_user(*user, credentials, callback_handler);
}

}
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2009-2014, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/bandwidth_manager.hpp"
#include "libtorrent/time.hpp"

namespace libtorrent
{

	bandwidth_manager::bandwidth_manager(int channel
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		, bool log
#endif
		)
		: m_queued_bytes(0)
		, m_channel(channel)
		, m_abort(false)
	{
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		if (log)
			m_log.open("bandwidth_limiter.log", std::ios::trunc);
		m_start = time_now();
#endif
	}

	void bandwidth_manager::close()
	{
		m_abort = true;
		m_queue.clear();
		m_queued_bytes = 0;
	}

#if TORRENT_USE_ASSERTS
	bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const
	{
		for (queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	int bandwidth_manager::queue_size() const
	{
		return m_queue.size();
	}

	int bandwidth_manager::queued_bytes() const
	{
		return m_queued_bytes;
	}

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	int bandwidth_manager::request_bandwidth(boost::intrusive_ptr<bandwidth_socket> const& peer
		, int blk, int priority
		, bandwidth_channel* chan1
		, bandwidth_channel* chan2
		, bandwidth_channel* chan3
		, bandwidth_channel* chan4
		, bandwidth_channel* chan5
		)
	{
		INVARIANT_CHECK;
		if (m_abort) return 0;

		TORRENT_ASSERT(blk > 0);
		TORRENT_ASSERT(priority > 0);
		TORRENT_ASSERT(!is_queued(peer.get()));

		bw_request bwr(peer, blk, priority);
		int i = 0;
		if (chan1 && chan1->throttle() > 0 && chan1->need_queueing(blk))
			bwr.channel[i++] = chan1;
		if (chan2 && chan2->throttle() > 0 && chan2->need_queueing(blk))
			bwr.channel[i++] = chan2;
		if (chan3 && chan3->throttle() > 0 && chan3->need_queueing(blk))
			bwr.channel[i++] = chan3;
		if (chan4 && chan4->throttle() > 0 && chan4->need_queueing(blk))
			bwr.channel[i++] = chan4;
		if (chan5 && chan5->throttle() > 0 && chan5->need_queueing(blk))
			bwr.channel[i++] = chan5;

		if (i == 0)
		{
			// the connection is not rate limited by any of its
			// bandwidth channels, or it doesn't belong to any
			// channels. There's no point in adding it to
			// the queue, just satisfy the request immediately
			return blk;
		}

		m_queued_bytes += blk;
		m_queue.push_back(bwr);
		return 0;
	}

#if TORRENT_USE_INVARIANT_CHECKS
	void bandwidth_manager::check_invariant() const
	{
		boost::int64_t queued = 0;
		for (queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			queued += i->request_size - i->assigned;
		}
		TORRENT_ASSERT(queued == m_queued_bytes);
	}
#endif

	void bandwidth_manager::update_quotas(time_duration const& dt)
	{
		if (m_abort) return;
		if (m_queue.empty()) return;

		INVARIANT_CHECK;

		int dt_milliseconds = total_milliseconds(dt);
		if (dt_milliseconds > 3000) dt_milliseconds = 3000;

		// for each bandwidth channel, call update_quota(dt)

		std::vector<bandwidth_channel*> channels;

		queue_t tm;

		for (queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			if (i->peer->is_disconnecting())
			{
				m_queued_bytes -= i->request_size - i->assigned;

				// return all assigned quota to all the
				// bandwidth channels this peer belongs to
				for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
				{
					bandwidth_channel* bwc = i->channel[j];
					bwc->return_quota(i->assigned);
				}

				i->assigned = 0;
				tm.push_back(*i);
				i = m_queue.erase(i);
				continue;
			}
			for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				bwc->tmp = 0;
			}
			++i;
		}

		for (queue_t::iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				if (bwc->tmp == 0) channels.push_back(bwc);
				TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority);
				bwc->tmp += i->priority;
			}
		}

		for (std::vector<bandwidth_channel*>::iterator i = channels.begin()
			, end(channels.end()); i != end; ++i)
		{
			(*i)->update_quota(dt_milliseconds);
		}

		for (queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			int a = i->assign_bandwidth();
			if (i->assigned == i->request_size
				|| (i->ttl <= 0 && i->assigned > 0))
			{
				a += i->request_size - i->assigned;
				TORRENT_ASSERT(i->assigned <= i->request_size);
				tm.push_back(*i);
				i = m_queue.erase(i);
			}
			else
			{
				++i;
			}
			m_queued_bytes -= a;
		}

		while (!tm.empty())
		{
			bw_request& bwr = tm.back();
			bwr.peer->assign_bandwidth(m_channel, bwr.assigned);
			tm.pop_back();
		}
	}
}

<commit_msg>back-port shutdown assert fix<commit_after>/*

Copyright (c) 2009-2014, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/bandwidth_manager.hpp"
#include "libtorrent/time.hpp"

namespace libtorrent
{

	bandwidth_manager::bandwidth_manager(int channel
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		, bool log
#endif
		)
		: m_queued_bytes(0)
		, m_channel(channel)
		, m_abort(false)
	{
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		if (log)
			m_log.open("bandwidth_limiter.log", std::ios::trunc);
		m_start = time_now();
#endif
	}

	void bandwidth_manager::close()
	{
		m_abort = true;

		queue_t tm;
		tm.swap(m_queue);
		m_queued_bytes = 0;

		while (!tm.empty())
		{
			bw_request& bwr = tm.back();
			bwr.peer->assign_bandwidth(m_channel, bwr.assigned);
			tm.pop_back();
		}
	}

#if TORRENT_USE_ASSERTS
	bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const
	{
		for (queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	int bandwidth_manager::queue_size() const
	{
		return m_queue.size();
	}

	int bandwidth_manager::queued_bytes() const
	{
		return m_queued_bytes;
	}

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	int bandwidth_manager::request_bandwidth(boost::intrusive_ptr<bandwidth_socket> const& peer
		, int blk, int priority
		, bandwidth_channel* chan1
		, bandwidth_channel* chan2
		, bandwidth_channel* chan3
		, bandwidth_channel* chan4
		, bandwidth_channel* chan5
		)
	{
		INVARIANT_CHECK;
		if (m_abort) return 0;

		TORRENT_ASSERT(blk > 0);
		TORRENT_ASSERT(priority > 0);
		TORRENT_ASSERT(!is_queued(peer.get()));

		bw_request bwr(peer, blk, priority);
		int i = 0;
		if (chan1 && chan1->throttle() > 0 && chan1->need_queueing(blk))
			bwr.channel[i++] = chan1;
		if (chan2 && chan2->throttle() > 0 && chan2->need_queueing(blk))
			bwr.channel[i++] = chan2;
		if (chan3 && chan3->throttle() > 0 && chan3->need_queueing(blk))
			bwr.channel[i++] = chan3;
		if (chan4 && chan4->throttle() > 0 && chan4->need_queueing(blk))
			bwr.channel[i++] = chan4;
		if (chan5 && chan5->throttle() > 0 && chan5->need_queueing(blk))
			bwr.channel[i++] = chan5;

		if (i == 0)
		{
			// the connection is not rate limited by any of its
			// bandwidth channels, or it doesn't belong to any
			// channels. There's no point in adding it to
			// the queue, just satisfy the request immediately
			return blk;
		}

		m_queued_bytes += blk;
		m_queue.push_back(bwr);
		return 0;
	}

#if TORRENT_USE_INVARIANT_CHECKS
	void bandwidth_manager::check_invariant() const
	{
		boost::int64_t queued = 0;
		for (queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			queued += i->request_size - i->assigned;
		}
		TORRENT_ASSERT(queued == m_queued_bytes);
	}
#endif

	void bandwidth_manager::update_quotas(time_duration const& dt)
	{
		if (m_abort) return;
		if (m_queue.empty()) return;

		INVARIANT_CHECK;

		int dt_milliseconds = total_milliseconds(dt);
		if (dt_milliseconds > 3000) dt_milliseconds = 3000;

		// for each bandwidth channel, call update_quota(dt)

		std::vector<bandwidth_channel*> channels;

		queue_t tm;

		for (queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			if (i->peer->is_disconnecting())
			{
				m_queued_bytes -= i->request_size - i->assigned;

				// return all assigned quota to all the
				// bandwidth channels this peer belongs to
				for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
				{
					bandwidth_channel* bwc = i->channel[j];
					bwc->return_quota(i->assigned);
				}

				i->assigned = 0;
				tm.push_back(*i);
				i = m_queue.erase(i);
				continue;
			}
			for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				bwc->tmp = 0;
			}
			++i;
		}

		for (queue_t::iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j)
			{
				bandwidth_channel* bwc = i->channel[j];
				if (bwc->tmp == 0) channels.push_back(bwc);
				TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority);
				bwc->tmp += i->priority;
			}
		}

		for (std::vector<bandwidth_channel*>::iterator i = channels.begin()
			, end(channels.end()); i != end; ++i)
		{
			(*i)->update_quota(dt_milliseconds);
		}

		for (queue_t::iterator i = m_queue.begin();
			i != m_queue.end();)
		{
			int a = i->assign_bandwidth();
			if (i->assigned == i->request_size
				|| (i->ttl <= 0 && i->assigned > 0))
			{
				a += i->request_size - i->assigned;
				TORRENT_ASSERT(i->assigned <= i->request_size);
				tm.push_back(*i);
				i = m_queue.erase(i);
			}
			else
			{
				++i;
			}
			m_queued_bytes -= a;
		}

		while (!tm.empty())
		{
			bw_request& bwr = tm.back();
			bwr.peer->assign_bandwidth(m_channel, bwr.assigned);
			tm.pop_back();
		}
	}
}

<|endoftext|>


<commit_before>#include "pch.h"
#include "axl_io_SharedMemoryTransport.h"
#include "axl_rtl_String.h"
#include "axl_err_Error.h"

namespace axl {
namespace io {

//.............................................................................

CSharedMemoryTransportBase::CSharedMemoryTransportBase ()
{
	m_Flags = 0;
	m_pHdr = NULL;
	m_pData = NULL;
	m_MappingSize = 0;
	m_PendingReqCount = 0;
}

bool
CSharedMemoryTransportBase::Open (
	const char* pFileName,
	const char* pReadEventName,
	const char* pWriteEventName,
	uint_t Flags
	)
{
	Close ();

	bool Result = m_File.Open (pFileName, io::EFileFlag_ShareWrite);
	if (!Result)
		return false;

	Result = EnsureMappingSize (ESharedMemoryTransport_DefMappingSize);
	if (!Result)
	{
		m_File.Close ();
		return false;
	}

	if (Flags & ESharedMemoryTransportFlag_Create)
	{
		m_pHdr->m_Signature = ESharedMemoryTransport_FileSignature;
		m_pHdr->m_Lock = 0;
		m_pHdr->m_State = ESharedMemoryTransportState_MasterConnected;
		m_pHdr->m_ReadOffset = 0;
		m_pHdr->m_WriteOffset = 0;
		m_pHdr->m_EndOffset = 0;
		m_pHdr->m_DataSize = 0;

#if (_AXL_ENV == AXL_ENV_WIN)
		Result =
			m_ReadEvent.Create (NULL, false, false, rtl::CString_utf16 (pReadEventName)) &&
			m_WriteEvent.Create (NULL, false, false, rtl::CString_utf16 (pWriteEventName));
#elif (_AXL_ENV == AXL_ENV_POSIX)
		Result = m_ReadEvent.Open (pReadEventName, O_CREAT);
		if (Result)
		{
			m_ReadEventName = pReadEventName;

			Result = m_WriteEvent.Open (pWriteEventName, O_CREAT);
			if (Result)
				m_WriteEventName = pWriteEventName;
		}
#endif
	}
	else
	{
		if (m_pHdr->m_Signature != ESharedMemoryTransport_FileSignature)
		{
			err::SetError (err::EStatus_InvalidParameter);
			return false;
		}

		mt::AtomicLock (&m_pHdr->m_Lock);

		if (m_pHdr->m_State != ESharedMemoryTransportState_MasterConnected ||
			m_pHdr->m_ReadOffset != 0)
		{
			err::SetError (err::EStatus_InvalidDeviceState);
			return false;
		}

		m_pHdr->m_State = ESharedMemoryTransportState_SlaveConnected;

		mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
		Result =
			m_ReadEvent.Open (EVENT_ALL_ACCESS, false, rtl::CString_utf16 (pReadEventName)) &&
			m_WriteEvent.Open (EVENT_ALL_ACCESS, false, rtl::CString_utf16 (pWriteEventName));
#elif (_AXL_ENV == AXL_ENV_POSIX)
		Result =
			m_ReadEvent.Open (pReadEventName, 0) &&
			m_WriteEvent.Open (pWriteEventName, 0);
#endif
	}

	if (!Result)
	{
		Close ();
		return false;
	}

	m_Flags = Flags;
	return true;
}

void
CSharedMemoryTransportBase::Close ()
{
	if (!IsOpen ())
		return;

	Disconnect ();

	m_File.Close ();
	m_ReadEvent.Close ();
	m_WriteEvent.Close ();
	m_pHdr = NULL;
	m_pData = NULL;
	m_MappingSize = 0;
	m_PendingReqCount = 0;

#if (_AXL_ENV == AXL_ENV_POSIX)
	if (!m_ReadEventName.IsEmpty ())
	{
		mt::psx::CSem::Unlink (m_ReadEventName);
		m_ReadEventName.Clear ();
	}

	if (!m_WriteEventName.IsEmpty ())
	{
		mt::psx::CSem::Unlink (m_WriteEventName);
		m_WriteEventName.Clear ();
	}
#endif
}

void
CSharedMemoryTransportBase::Disconnect ()
{
	mt::AtomicLock (&m_pHdr->m_Lock);
	m_pHdr->m_State = ESharedMemoryTransportState_Disconnected;

#if (_AXL_ENV == AXL_ENV_WIN)
	m_ReadEvent.Signal ();
	m_WriteEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_ReadEvent.Post ();
	m_WriteEvent.Post ();
#endif

	mt::AtomicUnlock (&m_pHdr->m_Lock);
}

bool
CSharedMemoryTransportBase::EnsureMappingSize (size_t Size)
{
	if (Size <= m_MappingSize)
		return true;

	void* p = m_File.View (0, Size);
	if (!p)
		return false;

	m_pHdr = (TSharedMemoryTransportHdr*) p;
	m_pData = (char*) (m_pHdr + 1);
	m_MappingSize = Size;
	return true;
}

//.............................................................................

rtl::CArrayT <char>
CSharedMemoryReader::Read ()
{
	rtl::CArrayT <char> Buffer;
	Read (&Buffer);
	return Buffer;
}

size_t
CSharedMemoryReader::Read (rtl::CArrayT <char>* pBuffer)
{
	ASSERT (IsOpen ());

	mt::AtomicLock (&m_pHdr->m_Lock);

	// if buffer is empty, then wait until we have any data

	while (
		!m_pHdr->m_DataSize &&
		m_pHdr->m_State != ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		m_WriteEvent.Wait ();
		mt::AtomicLock (&m_pHdr->m_Lock);
	}

	if (m_pHdr->m_State == ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		err::SetError (err::EStatus_InvalidDeviceState);
		return -1;
	}

	size_t ReadOffset = m_pHdr->m_ReadOffset;
	size_t WriteOffset = m_pHdr->m_WriteOffset;
	size_t EndOffset = m_pHdr->m_EndOffset;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

	bool Result = EnsureOffsetMapped (EndOffset);
	if (!Result)
		return -1;

	size_t ReadSize = 0;

	if (m_Flags & ESharedMemoryTransportFlag_Message)
	{
		TSharedMemoryTransportMessageHdr* pMsgHdr = (TSharedMemoryTransportMessageHdr*) (m_pData + ReadOffset);
		ReadSize = pMsgHdr->m_Size;
		size_t ReadEndOffset = ReadOffset + sizeof (TSharedMemoryTransportMessageHdr) + pMsgHdr->m_Size;

		if (pMsgHdr->m_Signature != ESharedMemoryTransport_MessageSignature || ReadEndOffset > EndOffset)
		{
			err::SetError (err::EStatus_InvalidParameter);
			return -1;
		}

		pBuffer->Copy ((const char*) (pMsgHdr + 1), ReadSize);

		mt::AtomicLock (&m_pHdr->m_Lock);
		ASSERT (ReadEndOffset <= m_pHdr->m_EndOffset);

		m_pHdr->m_ReadOffset = m_pHdr->m_EndOffset != m_pHdr->m_WriteOffset && ReadEndOffset >= m_pHdr->m_EndOffset ?
			0 : ReadEndOffset; // wrap : no wrap
	}
	else
	{
		if (ReadOffset < WriteOffset)
		{
			ReadSize = WriteOffset - ReadOffset;
			pBuffer->Copy (m_pData + ReadOffset, ReadSize);
		}
		else
		{
			size_t Size1 = EndOffset - ReadOffset;
			size_t Size2 = WriteOffset;
			ReadSize = Size1 + Size2;

			pBuffer->SetCount (ReadSize);

			if (Size1)
				memcpy (*pBuffer, m_pData + ReadOffset, Size1);

			if (Size2)
				memcpy (*pBuffer + Size1, m_pData + 1, Size2);
		}

		mt::AtomicLock (&m_pHdr->m_Lock);
		m_pHdr->m_ReadOffset = WriteOffset;
	}

	m_pHdr->m_DataSize -= ReadSize;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
	m_ReadEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_ReadEvent.Post ();
#endif

	return ReadSize;
}

//.............................................................................

bool
CSharedMemoryWriter::Open (
	const char* pFileName,
	const char* pReadEventName,
	const char* pWriteEventName,
	uint_t Flags,
	size_t SizeLimitHint
	)
{
	Close ();
	m_SizeLimitHint = SizeLimitHint;
	return CSharedMemoryTransportBase::Open (pFileName, pReadEventName, pWriteEventName, Flags);
}

size_t
CSharedMemoryWriter::Write (
	const void* const* pBlockArray,
	const size_t* pSizeArray,
	size_t Count
	)
{
	ASSERT (IsOpen ());

	size_t ChainSize = 0;

	for (size_t i = 0; i < Count; i++)
		ChainSize += pSizeArray [i];

	if (!ChainSize)
		return 0;

	size_t WriteSize = ChainSize;

	if (m_Flags & ESharedMemoryTransportFlag_Message)
		WriteSize += sizeof (TSharedMemoryTransportMessageHdr);

	mt::CScopeLock ScopeLock (&m_WriteLock); // ensure atomic write

	mt::AtomicLock (&m_pHdr->m_Lock);

	// if buffer is wrapped, then wait until we have enough space

	while (
		m_pHdr->m_WriteOffset <= m_pHdr->m_ReadOffset && m_pHdr->m_DataSize && // wrapped
		m_pHdr->m_WriteOffset + WriteSize > m_pHdr->m_ReadOffset &&
		m_pHdr->m_State != ESharedMemoryTransportState_Disconnected
		)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		m_ReadEvent.Wait ();
		mt::AtomicLock (&m_pHdr->m_Lock);
	}

	if (m_pHdr->m_State == ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		err::SetError (err::EStatus_InvalidDeviceState);
		return -1;
	}

	size_t ReadOffset = m_pHdr->m_ReadOffset;
	size_t WriteOffset = m_pHdr->m_WriteOffset;
	size_t DataSize = m_pHdr->m_DataSize;

	mt::AtomicUnlock (&m_pHdr->m_Lock);

	size_t WriteEndOffset = WriteOffset + WriteSize;

	EnsureOffsetMapped (WriteEndOffset);

	if (m_Flags & ESharedMemoryTransportFlag_Message)
	{
		TSharedMemoryTransportMessageHdr* pMsgHdr = (TSharedMemoryTransportMessageHdr*) (m_pData + WriteOffset);
		pMsgHdr->m_Signature = ESharedMemoryTransport_MessageSignature;
		pMsgHdr->m_Size = ChainSize;
		CopyWriteChain (pMsgHdr + 1, pBlockArray, pSizeArray, Count);
	}
	else
	{
		CopyWriteChain (m_pData + WriteOffset, pBlockArray, pSizeArray, Count);
	}

	mt::AtomicLock (&m_pHdr->m_Lock);

	if (m_pHdr->m_WriteOffset <= m_pHdr->m_ReadOffset && m_pHdr->m_DataSize) // wrapped
	{
		m_pHdr->m_WriteOffset = WriteEndOffset;
	}
	else
	{
		m_pHdr->m_WriteOffset = m_pHdr->m_DataSize > m_SizeLimitHint ? 0 : WriteEndOffset;
		m_pHdr->m_EndOffset = WriteEndOffset;
	}

	m_pHdr->m_DataSize += ChainSize;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
	m_WriteEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_WriteEvent.Post ();
#endif

	return ChainSize;
}

void
CSharedMemoryWriter::CopyWriteChain (
	void* _pDst,
	const void* const* pBlockArray,
	const size_t* pSizeArray,
	size_t Count
	)
{
	char* pDst = (char*) _pDst;

	for (size_t i = 0; i < Count; i++)
	{
		const void* pSrc = pBlockArray [i];
		size_t Size = pSizeArray [i];

		memcpy (pDst, pSrc, Size);
		pDst += Size;
	}
}

//.............................................................................

} // namespace axl {
} // namespace io {
<commit_msg>[axl.io] shared memory transport uses io::EFileFlag_DeleteOnClose when creating transport file<commit_after>#include "pch.h"
#include "axl_io_SharedMemoryTransport.h"
#include "axl_rtl_String.h"
#include "axl_err_Error.h"

namespace axl {
namespace io {

//.............................................................................

CSharedMemoryTransportBase::CSharedMemoryTransportBase ()
{
	m_Flags = 0;
	m_pHdr = NULL;
	m_pData = NULL;
	m_MappingSize = 0;
	m_PendingReqCount = 0;
}

bool
CSharedMemoryTransportBase::Open (
	const char* pFileName,
	const char* pReadEventName,
	const char* pWriteEventName,
	uint_t Flags
	)
{
	Close ();

	uint_t FileFlags = io::EFileFlag_ShareWrite;
	if (Flags & ESharedMemoryTransportFlag_Create)
		FileFlags |= io::EFileFlag_DeleteOnClose;

	bool Result = m_File.Open (pFileName, FileFlags);
	if (!Result)
		return false;

	Result = EnsureMappingSize (ESharedMemoryTransport_DefMappingSize);
	if (!Result)
	{
		m_File.Close ();
		return false;
	}

	if (Flags & ESharedMemoryTransportFlag_Create)
	{
		m_pHdr->m_Signature = ESharedMemoryTransport_FileSignature;
		m_pHdr->m_Lock = 0;
		m_pHdr->m_State = ESharedMemoryTransportState_MasterConnected;
		m_pHdr->m_ReadOffset = 0;
		m_pHdr->m_WriteOffset = 0;
		m_pHdr->m_EndOffset = 0;
		m_pHdr->m_DataSize = 0;

#if (_AXL_ENV == AXL_ENV_WIN)
		Result =
			m_ReadEvent.Create (NULL, false, false, rtl::CString_utf16 (pReadEventName)) &&
			m_WriteEvent.Create (NULL, false, false, rtl::CString_utf16 (pWriteEventName));
#elif (_AXL_ENV == AXL_ENV_POSIX)
		Result = m_ReadEvent.Open (pReadEventName, O_CREAT);
		if (Result)
		{
			m_ReadEventName = pReadEventName;

			Result = m_WriteEvent.Open (pWriteEventName, O_CREAT);
			if (Result)
				m_WriteEventName = pWriteEventName;
		}
#endif
	}
	else
	{
		if (m_pHdr->m_Signature != ESharedMemoryTransport_FileSignature)
		{
			err::SetError (err::EStatus_InvalidParameter);
			return false;
		}

		mt::AtomicLock (&m_pHdr->m_Lock);

		if (m_pHdr->m_State != ESharedMemoryTransportState_MasterConnected ||
			m_pHdr->m_ReadOffset != 0)
		{
			err::SetError (err::EStatus_InvalidDeviceState);
			return false;
		}

		m_pHdr->m_State = ESharedMemoryTransportState_SlaveConnected;

		mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
		Result =
			m_ReadEvent.Open (EVENT_ALL_ACCESS, false, rtl::CString_utf16 (pReadEventName)) &&
			m_WriteEvent.Open (EVENT_ALL_ACCESS, false, rtl::CString_utf16 (pWriteEventName));
#elif (_AXL_ENV == AXL_ENV_POSIX)
		Result =
			m_ReadEvent.Open (pReadEventName, 0) &&
			m_WriteEvent.Open (pWriteEventName, 0);
#endif
	}

	if (!Result)
	{
		Close ();
		return false;
	}

	m_Flags = Flags;
	return true;
}

void
CSharedMemoryTransportBase::Close ()
{
	if (!IsOpen ())
		return;

	Disconnect ();

	m_File.Close ();
	m_ReadEvent.Close ();
	m_WriteEvent.Close ();
	m_pHdr = NULL;
	m_pData = NULL;
	m_MappingSize = 0;
	m_PendingReqCount = 0;

#if (_AXL_ENV == AXL_ENV_POSIX)
	if (!m_ReadEventName.IsEmpty ())
	{
		mt::psx::CSem::Unlink (m_ReadEventName);
		m_ReadEventName.Clear ();
	}

	if (!m_WriteEventName.IsEmpty ())
	{
		mt::psx::CSem::Unlink (m_WriteEventName);
		m_WriteEventName.Clear ();
	}
#endif
}

void
CSharedMemoryTransportBase::Disconnect ()
{
	mt::AtomicLock (&m_pHdr->m_Lock);
	m_pHdr->m_State = ESharedMemoryTransportState_Disconnected;

#if (_AXL_ENV == AXL_ENV_WIN)
	m_ReadEvent.Signal ();
	m_WriteEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_ReadEvent.Post ();
	m_WriteEvent.Post ();
#endif

	mt::AtomicUnlock (&m_pHdr->m_Lock);
}

bool
CSharedMemoryTransportBase::EnsureMappingSize (size_t Size)
{
	if (Size <= m_MappingSize)
		return true;

	void* p = m_File.View (0, Size);
	if (!p)
		return false;

	m_pHdr = (TSharedMemoryTransportHdr*) p;
	m_pData = (char*) (m_pHdr + 1);
	m_MappingSize = Size;
	return true;
}

//.............................................................................

rtl::CArrayT <char>
CSharedMemoryReader::Read ()
{
	rtl::CArrayT <char> Buffer;
	Read (&Buffer);
	return Buffer;
}

size_t
CSharedMemoryReader::Read (rtl::CArrayT <char>* pBuffer)
{
	ASSERT (IsOpen ());

	mt::AtomicLock (&m_pHdr->m_Lock);

	// if buffer is empty, then wait until we have any data

	while (
		!m_pHdr->m_DataSize &&
		m_pHdr->m_State != ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		m_WriteEvent.Wait ();
		mt::AtomicLock (&m_pHdr->m_Lock);
	}

	if (m_pHdr->m_State == ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		err::SetError (err::EStatus_InvalidDeviceState);
		return -1;
	}

	size_t ReadOffset = m_pHdr->m_ReadOffset;
	size_t WriteOffset = m_pHdr->m_WriteOffset;
	size_t EndOffset = m_pHdr->m_EndOffset;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

	bool Result = EnsureOffsetMapped (EndOffset);
	if (!Result)
		return -1;

	size_t ReadSize = 0;

	if (m_Flags & ESharedMemoryTransportFlag_Message)
	{
		TSharedMemoryTransportMessageHdr* pMsgHdr = (TSharedMemoryTransportMessageHdr*) (m_pData + ReadOffset);
		ReadSize = pMsgHdr->m_Size;
		size_t ReadEndOffset = ReadOffset + sizeof (TSharedMemoryTransportMessageHdr) + pMsgHdr->m_Size;

		if (pMsgHdr->m_Signature != ESharedMemoryTransport_MessageSignature || ReadEndOffset > EndOffset)
		{
			err::SetError (err::EStatus_InvalidParameter);
			return -1;
		}

		pBuffer->Copy ((const char*) (pMsgHdr + 1), ReadSize);

		mt::AtomicLock (&m_pHdr->m_Lock);
		ASSERT (ReadEndOffset <= m_pHdr->m_EndOffset);

		m_pHdr->m_ReadOffset = m_pHdr->m_EndOffset != m_pHdr->m_WriteOffset && ReadEndOffset >= m_pHdr->m_EndOffset ?
			0 : ReadEndOffset; // wrap : no wrap
	}
	else
	{
		if (ReadOffset < WriteOffset)
		{
			ReadSize = WriteOffset - ReadOffset;
			pBuffer->Copy (m_pData + ReadOffset, ReadSize);
		}
		else
		{
			size_t Size1 = EndOffset - ReadOffset;
			size_t Size2 = WriteOffset;
			ReadSize = Size1 + Size2;

			pBuffer->SetCount (ReadSize);

			if (Size1)
				memcpy (*pBuffer, m_pData + ReadOffset, Size1);

			if (Size2)
				memcpy (*pBuffer + Size1, m_pData + 1, Size2);
		}

		mt::AtomicLock (&m_pHdr->m_Lock);
		m_pHdr->m_ReadOffset = WriteOffset;
	}

	m_pHdr->m_DataSize -= ReadSize;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
	m_ReadEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_ReadEvent.Post ();
#endif

	return ReadSize;
}

//.............................................................................

bool
CSharedMemoryWriter::Open (
	const char* pFileName,
	const char* pReadEventName,
	const char* pWriteEventName,
	uint_t Flags,
	size_t SizeLimitHint
	)
{
	Close ();
	m_SizeLimitHint = SizeLimitHint;
	return CSharedMemoryTransportBase::Open (pFileName, pReadEventName, pWriteEventName, Flags);
}

size_t
CSharedMemoryWriter::Write (
	const void* const* pBlockArray,
	const size_t* pSizeArray,
	size_t Count
	)
{
	ASSERT (IsOpen ());

	size_t ChainSize = 0;

	for (size_t i = 0; i < Count; i++)
		ChainSize += pSizeArray [i];

	if (!ChainSize)
		return 0;

	size_t WriteSize = ChainSize;

	if (m_Flags & ESharedMemoryTransportFlag_Message)
		WriteSize += sizeof (TSharedMemoryTransportMessageHdr);

	mt::CScopeLock ScopeLock (&m_WriteLock); // ensure atomic write

	mt::AtomicLock (&m_pHdr->m_Lock);

	// if buffer is wrapped, then wait until we have enough space

	while (
		m_pHdr->m_WriteOffset <= m_pHdr->m_ReadOffset && m_pHdr->m_DataSize && // wrapped
		m_pHdr->m_WriteOffset + WriteSize > m_pHdr->m_ReadOffset &&
		m_pHdr->m_State != ESharedMemoryTransportState_Disconnected
		)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		m_ReadEvent.Wait ();
		mt::AtomicLock (&m_pHdr->m_Lock);
	}

	if (m_pHdr->m_State == ESharedMemoryTransportState_Disconnected)
	{
		mt::AtomicUnlock (&m_pHdr->m_Lock);
		err::SetError (err::EStatus_InvalidDeviceState);
		return -1;
	}

	size_t ReadOffset = m_pHdr->m_ReadOffset;
	size_t WriteOffset = m_pHdr->m_WriteOffset;
	size_t DataSize = m_pHdr->m_DataSize;

	mt::AtomicUnlock (&m_pHdr->m_Lock);

	size_t WriteEndOffset = WriteOffset + WriteSize;

	EnsureOffsetMapped (WriteEndOffset);

	if (m_Flags & ESharedMemoryTransportFlag_Message)
	{
		TSharedMemoryTransportMessageHdr* pMsgHdr = (TSharedMemoryTransportMessageHdr*) (m_pData + WriteOffset);
		pMsgHdr->m_Signature = ESharedMemoryTransport_MessageSignature;
		pMsgHdr->m_Size = ChainSize;
		CopyWriteChain (pMsgHdr + 1, pBlockArray, pSizeArray, Count);
	}
	else
	{
		CopyWriteChain (m_pData + WriteOffset, pBlockArray, pSizeArray, Count);
	}

	mt::AtomicLock (&m_pHdr->m_Lock);

	if (m_pHdr->m_WriteOffset <= m_pHdr->m_ReadOffset && m_pHdr->m_DataSize) // wrapped
	{
		m_pHdr->m_WriteOffset = WriteEndOffset;
	}
	else
	{
		m_pHdr->m_WriteOffset = m_pHdr->m_DataSize > m_SizeLimitHint ? 0 : WriteEndOffset;
		m_pHdr->m_EndOffset = WriteEndOffset;
	}

	m_pHdr->m_DataSize += ChainSize;
	mt::AtomicUnlock (&m_pHdr->m_Lock);

#if (_AXL_ENV == AXL_ENV_WIN)
	m_WriteEvent.Signal ();
#elif (_AXL_ENV == AXL_ENV_POSIX)
	m_WriteEvent.Post ();
#endif

	return ChainSize;
}

void
CSharedMemoryWriter::CopyWriteChain (
	void* _pDst,
	const void* const* pBlockArray,
	const size_t* pSizeArray,
	size_t Count
	)
{
	char* pDst = (char*) _pDst;

	for (size_t i = 0; i < Count; i++)
	{
		const void* pSrc = pBlockArray [i];
		size_t Size = pSizeArray [i];

		memcpy (pDst, pSrc, Size);
		pDst += Size;
	}
}

//.............................................................................

} // namespace axl {
} // namespace io {
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

			boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
			if (i->is_v4())
			{
				s->open(udp::v4(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
				s->set_option(outbound_interface(i->to_v4()), ec);
				if (ec) continue;
			}
			else
			{
				s->open(udp::v6(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
//				s->set_option(outbound_interface(i->to_v6()), ec);
//				if (ec) continue;
			}
			s->set_option(hops(255), ec);
			if (ec) continue;
			s->set_option(enable_loopback(loopback), ec);
			if (ec) continue;
			m_sockets.push_back(socket_entry(s));
			socket_entry& se = m_sockets.back();
			s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
				, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
		}

		open_unicast_socket(ios, address_v4::any());
		open_unicast_socket(ios, address_v6::any());
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		asio::error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		m_on_receive.clear();

		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));
	}
}

<commit_msg>broadcast socket fix<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

			boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
			if (i->is_v4())
			{
				s->open(udp::v4(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
				s->set_option(outbound_interface(i->to_v4()), ec);
				if (ec) continue;
			}
			else
			{
				s->open(udp::v6(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
//				s->set_option(outbound_interface(i->to_v6()), ec);
//				if (ec) continue;
			}
			s->set_option(hops(255), ec);
			if (ec) continue;
			s->set_option(enable_loopback(loopback), ec);
			if (ec) continue;
			m_sockets.push_back(socket_entry(s));
			socket_entry& se = m_sockets.back();
			s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
				, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
		}
		open_unicast_socket(ios, address_v4::any());
		open_unicast_socket(ios, address_v6::any());
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		asio::error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}

		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/version.hpp>

#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif

#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
#if TORRENT_USE_IPV6
		if (a.is_v6()) return a.to_v6().is_link_local();
#endif
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
#else
		return addr.to_v4() == address_v4::loopback();
#endif
	}

	bool is_multicast(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
#else
		return addr.to_v4().is_multicast();
#endif
	}

	bool is_any(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else if (addr.to_v6().is_v4_mapped())
			return (addr.to_v6().to_v4() == address_v4::any());
		else
			return addr.to_v6() == address_v6::any();
#else
		return addr.to_v4() == address_v4::any();
#endif
	}

	bool supports_ipv6()
	{
#if TORRENT_USE_IPV6
		error_code ec;
		address::from_string("::1", ec);
		return !ec;
#else
		return false;
#endif
	}

	address guess_local_address(io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		error_code ec;
		std::vector<ip_interface> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = i->interface_address;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	// count the length of the common bit prefix
	int common_bits(unsigned char const* b1
		, unsigned char const* b2, int n)
	{
		for (int i = 0; i < n; ++i, ++b1, ++b2)
		{
			unsigned char a = *b1 ^ *b2;
			if (a == 0) continue;
			int ret = i * 8 + 8;
			for (; a > 0; a >>= 1) --ret;
			return ret;
		}
		return n * 8;
	}

	// returns the number of bits in that differ from the right
	// between the addresses.
	int cidr_distance(address const& a1, address const& a2)
	{
#if TORRENT_USE_IPV6
		if (a1.is_v4() && a2.is_v4())
		{
#endif
			// both are v4
			address_v4::bytes_type b1 = a1.to_v4().to_bytes();
			address_v4::bytes_type b2 = a2.to_v4().to_bytes();
			return address_v4::bytes_type::static_size * 8
				- common_bits(b1.c_array(), b2.c_array(), b1.size());
#if TORRENT_USE_IPV6
		}

		address_v6::bytes_type b1;
		address_v6::bytes_type b2;
		if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes();
		else b1 = a1.to_v6().to_bytes();
		if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes();
		else b2 = a2.to_v6().to_bytes();
		return address_v6::bytes_type::static_size * 8
			- common_bits(b1.c_array(), b2.c_array(), b1.size());
#endif
	}

	broadcast_socket::broadcast_socket(io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		error_code ec;
		std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);

#if TORRENT_USE_IPV6
		if (multicast_endpoint.address().is_v4())
#endif
			open_multicast_socket(ios, address_v4::any(), loopback);
#if TORRENT_USE_IPV6
		else
			open_multicast_socket(ios, address_v6::any(), loopback);
#endif

		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only multicast on compatible networks
			if (i->interface_address.is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(i->interface_address)) continue;

#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->interface_address.to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
			open_unicast_socket(ios, i->interface_address);
		}
	}

	void broadcast_socket::open_multicast_socket(io_service& ios
		, address const& addr, bool loopback)
	{
		using namespace asio::ip::multicast;

		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->set_option(datagram_socket::reuse_address(true), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
		if (ec) return;
		s->set_option(join_group(m_multicast_endpoint.address()), ec);
		if (ec) return;
		s->set_option(hops(255), ec);
		if (ec) return;
		s->set_option(enable_loopback(loopback), ec);
		if (ec) return;
		m_sockets.push_back(socket_entry(s));
		socket_entry& se = m_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		using namespace asio::ip::multicast;
		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<commit_msg>include self-assigned address range in what's considered 'local'<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

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

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/version.hpp>

#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif

#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
#if TORRENT_USE_IPV6
		if (a.is_v6()) return a.to_v6().is_link_local();
#endif
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000 // 10.x.x.x
			|| (ip & 0xfff00000) == 0xac100000 // 172.16.x.x
			|| (ip & 0xffff0000) == 0xc0a80000 // 192.168.x.x
			|| (ip & 0xa9fe0000) == 0xa9fe0000); // 169.254.x.x
	}

	bool is_loopback(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
#else
		return addr.to_v4() == address_v4::loopback();
#endif
	}

	bool is_multicast(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
#else
		return addr.to_v4().is_multicast();
#endif
	}

	bool is_any(address const& addr)
	{
#if TORRENT_USE_IPV6
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else if (addr.to_v6().is_v4_mapped())
			return (addr.to_v6().to_v4() == address_v4::any());
		else
			return addr.to_v6() == address_v6::any();
#else
		return addr.to_v4() == address_v4::any();
#endif
	}

	bool supports_ipv6()
	{
#if TORRENT_USE_IPV6
		error_code ec;
		address::from_string("::1", ec);
		return !ec;
#else
		return false;
#endif
	}

	address guess_local_address(io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		error_code ec;
		std::vector<ip_interface> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = i->interface_address;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	// count the length of the common bit prefix
	int common_bits(unsigned char const* b1
		, unsigned char const* b2, int n)
	{
		for (int i = 0; i < n; ++i, ++b1, ++b2)
		{
			unsigned char a = *b1 ^ *b2;
			if (a == 0) continue;
			int ret = i * 8 + 8;
			for (; a > 0; a >>= 1) --ret;
			return ret;
		}
		return n * 8;
	}

	// returns the number of bits in that differ from the right
	// between the addresses.
	int cidr_distance(address const& a1, address const& a2)
	{
#if TORRENT_USE_IPV6
		if (a1.is_v4() && a2.is_v4())
		{
#endif
			// both are v4
			address_v4::bytes_type b1 = a1.to_v4().to_bytes();
			address_v4::bytes_type b2 = a2.to_v4().to_bytes();
			return address_v4::bytes_type::static_size * 8
				- common_bits(b1.c_array(), b2.c_array(), b1.size());
#if TORRENT_USE_IPV6
		}

		address_v6::bytes_type b1;
		address_v6::bytes_type b2;
		if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes();
		else b1 = a1.to_v6().to_bytes();
		if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes();
		else b2 = a2.to_v6().to_bytes();
		return address_v6::bytes_type::static_size * 8
			- common_bits(b1.c_array(), b2.c_array(), b1.size());
#endif
	}

	broadcast_socket::broadcast_socket(io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		error_code ec;
		std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);

#if TORRENT_USE_IPV6
		if (multicast_endpoint.address().is_v4())
#endif
			open_multicast_socket(ios, address_v4::any(), loopback);
#if TORRENT_USE_IPV6
		else
			open_multicast_socket(ios, address_v6::any(), loopback);
#endif

		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only multicast on compatible networks
			if (i->interface_address.is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(i->interface_address)) continue;

#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->interface_address.to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
			open_unicast_socket(ios, i->interface_address);
		}
	}

	void broadcast_socket::open_multicast_socket(io_service& ios
		, address const& addr, bool loopback)
	{
		using namespace asio::ip::multicast;

		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->set_option(datagram_socket::reuse_address(true), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
		if (ec) return;
		s->set_option(join_group(m_multicast_endpoint.address()), ec);
		if (ec) return;
		s->set_option(hops(255), ec);
		if (ec) return;
		s->set_option(enable_loopback(loopback), ec);
		if (ec) return;
		m_sockets.push_back(socket_entry(s));
		socket_entry& se = m_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		using namespace asio::ip::multicast;
		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<|endoftext|>