<commit_before>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Stock.hxx"
#include "Error.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "spawn/ChildStock.hxx"
#include "spawn/Prepared.hxx"
#include "spawn/ChildOptions.hxx"
#include "pool/DisposablePointer.hxx"
#include "pool/tpool.hxx"
#include "pool/StringBuilder.hxx"
#include "event/SocketEvent.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StringFormat.hxx"
#include "util/StringList.hxx"

#include <assert.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#ifdef __linux
#include <sched.h>
#endif

class FcgiStock final : StockClass, ChildStockClass {
	StockMap hstock;
	ChildStock child_stock;

public:
	FcgiStock(unsigned limit, unsigned max_idle,
		  EventLoop &event_loop, SpawnService &spawn_service,
		  SocketDescriptor _log_socket,
		  const ChildErrorLogOptions &_log_options) noexcept;

	~FcgiStock() noexcept {
		/* this one must be cleared before #child_stock; FadeAll()
		   calls ClearIdle(), so this method is the best match for
		   what we want to do (though a kludge) */
		hstock.FadeAll();
	}

	EventLoop &GetEventLoop() noexcept {
		return hstock.GetEventLoop();
	}

	SocketDescriptor GetLogSocket() const noexcept {
		return child_stock.GetLogSocket();
	}

	const auto &GetLogOptions() const noexcept {
		return child_stock.GetLogOptions();
	}

	StockItem *Get(const ChildOptions &options,
		       const char *executable_path,
		       ConstBuffer<const char *> args);

	void FadeAll() noexcept {
		hstock.FadeAll();
		child_stock.GetStockMap().FadeAll();
	}

	void FadeTag(StringView tag) noexcept;

private:
	/* virtual methods from class StockClass */
	void Create(CreateStockItem c, StockRequest request,
		    CancellablePointer &cancel_ptr) override;

	/* virtual methods from class ChildStockClass */
	Event::Duration GetChildClearInterval(void *info) const noexcept override;
	bool WantReturnStderr(void *info) const noexcept override;
	bool WantStderrPond(void *info) const noexcept override;
	StringView GetChildTag(void *info) const noexcept override;
	void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
			  PreparedChildProcess &p) override;
};

struct FcgiChildParams {
	const char *executable_path;

	ConstBuffer<const char *> args;

	const ChildOptions &options;

	FcgiChildParams(const char *_executable_path,
			ConstBuffer<const char *> _args,
			const ChildOptions &_options) noexcept
		:executable_path(_executable_path), args(_args),
		 options(_options) {}

	const char *GetStockKey(struct pool &pool) const noexcept;
};

struct FcgiConnection final : StockItem {
	const LLogger logger;

	StockItem *child = nullptr;

	UniqueSocketDescriptor fd;
	SocketEvent event;

	UniqueFileDescriptor stderr_fd;

	/**
	 * Is this a fresh connection to the FastCGI child process?
	 */
	bool fresh = true;

	/**
	 * Shall the FastCGI child process be killed?
	 */
	bool kill = false;

	/**
	 * Was the current request aborted by the fcgi_client caller?
	 */
	bool aborted = false;

	explicit FcgiConnection(EventLoop &event_loop, CreateStockItem c) noexcept
		:StockItem(c), logger(GetStockName()),
		 event(event_loop, BIND_THIS_METHOD(OnSocketEvent)) {}

	~FcgiConnection() noexcept override;

	[[gnu::pure]]
	StringView GetTag() const noexcept {
		assert(child != nullptr);

		return child_stock_item_get_tag(*child);
	}

	UniqueFileDescriptor GetStderr() const noexcept {
		assert(child != nullptr);

		return child_stock_item_get_stderr(*child);
	}

	void SetSite(const char *site) noexcept {
		child_stock_item_set_site(*child, site);
	}

	void SetUri(const char *uri) noexcept {
		child_stock_item_set_uri(*child, uri);
	}

	/* virtual methods from class StockItem */
	bool Borrow() noexcept override;
	bool Release() noexcept override;

private:
	void OnSocketEvent(unsigned events) noexcept;
};

const char *
FcgiChildParams::GetStockKey(struct pool &pool) const noexcept
{
	PoolStringBuilder<256> b;
	b.push_back(executable_path);

	for (auto i : args) {
		b.push_back(" ");
		b.push_back(i);
	}

	for (auto i : options.env) {
		b.push_back("$");
		b.push_back(i);
	}

	char options_buffer[16384];
	b.emplace_back(options_buffer,
		       options.MakeId(options_buffer));

	return b(pool);
}

/*
 * libevent callback
 *
 */

void
FcgiConnection::OnSocketEvent(unsigned) noexcept
{
	char buffer;
	ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
	if (nbytes < 0)
		logger(2, "error on idle FastCGI connection: ", strerror(errno));
	else if (nbytes > 0)
		logger(2, "unexpected data from idle FastCGI connection");

	InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

Event::Duration
FcgiStock::GetChildClearInterval(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;

	return params.options.ns.mount.pivot_root == nullptr
		? std::chrono::minutes(10)
		/* lower clear_interval for jailed (per-account?)
		   processes */
		: std::chrono::minutes(5);
}

bool
FcgiStock::WantReturnStderr(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;
	/* we need the child process to return the stderr_fd to us if
	   the given path is "jailed" */
	return params.options.stderr_path != nullptr &&
		params.options.stderr_jailed;
}

bool
FcgiStock::WantStderrPond(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;
	return params.options.stderr_pond;
}

StringView
FcgiStock::GetChildTag(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;

	return params.options.tag;
}

void
FcgiStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
			PreparedChildProcess &p)
{
	auto &params = *(FcgiChildParams *)info;
	const ChildOptions &options = params.options;

	p.SetStdin(std::move(fd));

	/* the FastCGI protocol defines a channel for stderr, so we could
	   close its "real" stderr here, but many FastCGI applications
	   don't use the FastCGI protocol to send error messages, so we
	   just keep it open */

	UniqueFileDescriptor null_fd;
	if (null_fd.Open("/dev/null", O_WRONLY))
		p.SetStdout(std::move(null_fd));

	p.Append(params.executable_path);
	for (auto i : params.args)
		p.Append(i);

	options.CopyTo(p);
}

/*
 * stock class
 *
 */

void
FcgiStock::Create(CreateStockItem c, StockRequest request,
		  [[maybe_unused]] CancellablePointer &cancel_ptr)
{
	[[maybe_unused]] auto &params = *(FcgiChildParams *)request.get();
	assert(params.executable_path != nullptr);

	auto *connection = new FcgiConnection(GetEventLoop(), c);

	const char *key = c.GetStockName();

	try {
		connection->child = child_stock.GetStockMap().GetNow(key,
								     std::move(request));
	} catch (...) {
		delete connection;
		std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to start FastCGI server '%s'",
									 key)));
	}

	try {
		connection->fd = child_stock_item_connect(*connection->child);
	} catch (...) {
		connection->kill = true;
		delete connection;
		std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to connect to FastCGI server '%s'",
									 key)));
	}

	connection->event.Open(connection->fd);

	connection->InvokeCreateSuccess();
}

bool
FcgiConnection::Borrow() noexcept
{
	/* check the connection status before using it, just in case the
	   FastCGI server has decided to close the connection before
	   fcgi_connection_event_callback() got invoked */
	char buffer;
	ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
	if (nbytes > 0) {
		logger(2, "unexpected data from idle FastCGI connection");
		return false;
	} else if (nbytes == 0) {
		/* connection closed (not worth a log message) */
		return false;
	} else if (errno != EAGAIN) {
		logger(2, "error on idle FastCGI connection: ", strerror(errno));
		return false;
	}

	event.Cancel();
	aborted = false;
	return true;
}

bool
FcgiConnection::Release() noexcept
{
	fresh = false;
	event.ScheduleRead();
	return true;
}

FcgiConnection::~FcgiConnection() noexcept
{
	if (fd.IsDefined()) {
		event.Cancel();
		fd.Close();
	}

	if (fresh && aborted)
		/* the fcgi_client caller has aborted the request before the
		   first response on a fresh connection was received: better
		   kill the child process, it may be failing on us
		   completely */
		kill = true;

	if (child != nullptr)
		child->Put(kill);
}

/*
 * interface
 *
 */

inline
FcgiStock::FcgiStock(unsigned limit, unsigned max_idle,
		     EventLoop &event_loop, SpawnService &spawn_service,
		     SocketDescriptor _log_socket,
		     const ChildErrorLogOptions &_log_options) noexcept
	:hstock(event_loop, *this, limit, max_idle,
		std::chrono::minutes(2)),
	 child_stock(event_loop, spawn_service,
		     *this,
		     4,
		     _log_socket, _log_options,
		     limit, max_idle) {}

void
FcgiStock::FadeTag(StringView tag) noexcept
{
	assert(tag != nullptr);

	hstock.FadeIf([tag](const StockItem &item){
		const auto &connection = (const FcgiConnection &)item;
		return StringListContains(connection.GetTag(), '\0', tag);
	});

	child_stock.FadeTag(tag);
}

FcgiStock *
fcgi_stock_new(unsigned limit, unsigned max_idle,
	       EventLoop &event_loop, SpawnService &spawn_service,
	       SocketDescriptor log_socket,
	       const ChildErrorLogOptions &log_options) noexcept
{
	return new FcgiStock(limit, max_idle, event_loop, spawn_service,
			     log_socket, log_options);
}

void
fcgi_stock_free(FcgiStock *fcgi_stock) noexcept
{
	delete fcgi_stock;
}

SocketDescriptor
fcgi_stock_get_log_socket(const FcgiStock &fs) noexcept
{
	return fs.GetLogSocket();
}

const ChildErrorLogOptions &
fcgi_stock_get_log_options(const FcgiStock &fs) noexcept
{
	return fs.GetLogOptions();
}

void
fcgi_stock_fade_all(FcgiStock &fs) noexcept
{
	fs.FadeAll();
}

void
fcgi_stock_fade_tag(FcgiStock &fs, StringView tag) noexcept
{
	fs.FadeTag(tag);
}

inline StockItem *
FcgiStock::Get(const ChildOptions &options,
	       const char *executable_path,
	       ConstBuffer<const char *> args)
{
	const TempPoolLease tpool;

	auto r = NewDisposablePointer<FcgiChildParams>(*tpool, executable_path,
						       args, options);
	const char *key = r->GetStockKey(*tpool);
	return hstock.GetNow(key, std::move(r));
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock,
	       const ChildOptions &options,
	       const char *executable_path,
	       ConstBuffer<const char *> args)
{
	return fcgi_stock->Get(options, executable_path, args);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item) noexcept
{
	return AF_LOCAL;
}

UniqueFileDescriptor
fcgi_stock_item_get_stderr(const StockItem &item) noexcept
{
	const auto &connection = (const FcgiConnection &)item;
	return connection.GetStderr();
}

void
fcgi_stock_item_set_site(StockItem &item, const char *site) noexcept
{
	auto &connection = (FcgiConnection &)item;
	connection.SetSite(site);
}

void
fcgi_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
	auto &connection = (FcgiConnection &)item;
	connection.SetUri(uri);
}

SocketDescriptor
fcgi_stock_item_get(const StockItem &item) noexcept
{
	const auto *connection = (const FcgiConnection *)&item;

	assert(connection->fd.IsDefined());

	return connection->fd;
}

void
fcgi_stock_aborted(StockItem &item) noexcept
{
	auto *connection = (FcgiConnection *)&item;

	connection->aborted = true;
}
<commit_msg>fcgi/Stock: use class ChildStockItem<commit_after>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Stock.hxx"
#include "Error.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "spawn/ChildStock.hxx"
#include "spawn/ChildStockItem.hxx"
#include "spawn/Prepared.hxx"
#include "spawn/ChildOptions.hxx"
#include "pool/DisposablePointer.hxx"
#include "pool/tpool.hxx"
#include "pool/StringBuilder.hxx"
#include "event/SocketEvent.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "io/Logger.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StringFormat.hxx"
#include "util/StringList.hxx"

#include <assert.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#ifdef __linux
#include <sched.h>
#endif

class FcgiStock final : StockClass, ChildStockClass {
	StockMap hstock;
	ChildStock child_stock;

public:
	FcgiStock(unsigned limit, unsigned max_idle,
		  EventLoop &event_loop, SpawnService &spawn_service,
		  SocketDescriptor _log_socket,
		  const ChildErrorLogOptions &_log_options) noexcept;

	~FcgiStock() noexcept {
		/* this one must be cleared before #child_stock; FadeAll()
		   calls ClearIdle(), so this method is the best match for
		   what we want to do (though a kludge) */
		hstock.FadeAll();
	}

	EventLoop &GetEventLoop() noexcept {
		return hstock.GetEventLoop();
	}

	SocketDescriptor GetLogSocket() const noexcept {
		return child_stock.GetLogSocket();
	}

	const auto &GetLogOptions() const noexcept {
		return child_stock.GetLogOptions();
	}

	StockItem *Get(const ChildOptions &options,
		       const char *executable_path,
		       ConstBuffer<const char *> args);

	void FadeAll() noexcept {
		hstock.FadeAll();
		child_stock.GetStockMap().FadeAll();
	}

	void FadeTag(StringView tag) noexcept;

private:
	/* virtual methods from class StockClass */
	void Create(CreateStockItem c, StockRequest request,
		    CancellablePointer &cancel_ptr) override;

	/* virtual methods from class ChildStockClass */
	Event::Duration GetChildClearInterval(void *info) const noexcept override;
	bool WantReturnStderr(void *info) const noexcept override;
	bool WantStderrPond(void *info) const noexcept override;
	StringView GetChildTag(void *info) const noexcept override;
	void PrepareChild(void *info, UniqueSocketDescriptor &&fd,
			  PreparedChildProcess &p) override;
};

struct FcgiChildParams {
	const char *executable_path;

	ConstBuffer<const char *> args;

	const ChildOptions &options;

	FcgiChildParams(const char *_executable_path,
			ConstBuffer<const char *> _args,
			const ChildOptions &_options) noexcept
		:executable_path(_executable_path), args(_args),
		 options(_options) {}

	const char *GetStockKey(struct pool &pool) const noexcept;
};

struct FcgiConnection final : StockItem {
	const LLogger logger;

	ChildStockItem *child = nullptr;

	UniqueSocketDescriptor fd;
	SocketEvent event;

	UniqueFileDescriptor stderr_fd;

	/**
	 * Is this a fresh connection to the FastCGI child process?
	 */
	bool fresh = true;

	/**
	 * Shall the FastCGI child process be killed?
	 */
	bool kill = false;

	/**
	 * Was the current request aborted by the fcgi_client caller?
	 */
	bool aborted = false;

	explicit FcgiConnection(EventLoop &event_loop, CreateStockItem c) noexcept
		:StockItem(c), logger(GetStockName()),
		 event(event_loop, BIND_THIS_METHOD(OnSocketEvent)) {}

	~FcgiConnection() noexcept override;

	[[gnu::pure]]
	StringView GetTag() const noexcept {
		assert(child != nullptr);

		return child->GetTag();
	}

	UniqueFileDescriptor GetStderr() const noexcept {
		assert(child != nullptr);

		return child->GetStderr();
	}

	void SetSite(const char *site) noexcept {
		assert(child != nullptr);

		child->SetSite(site);
	}

	void SetUri(const char *uri) noexcept {
		assert(child != nullptr);

		child->SetUri(uri);
	}

	/* virtual methods from class StockItem */
	bool Borrow() noexcept override;
	bool Release() noexcept override;

private:
	void OnSocketEvent(unsigned events) noexcept;
};

const char *
FcgiChildParams::GetStockKey(struct pool &pool) const noexcept
{
	PoolStringBuilder<256> b;
	b.push_back(executable_path);

	for (auto i : args) {
		b.push_back(" ");
		b.push_back(i);
	}

	for (auto i : options.env) {
		b.push_back("$");
		b.push_back(i);
	}

	char options_buffer[16384];
	b.emplace_back(options_buffer,
		       options.MakeId(options_buffer));

	return b(pool);
}

/*
 * libevent callback
 *
 */

void
FcgiConnection::OnSocketEvent(unsigned) noexcept
{
	char buffer;
	ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
	if (nbytes < 0)
		logger(2, "error on idle FastCGI connection: ", strerror(errno));
	else if (nbytes > 0)
		logger(2, "unexpected data from idle FastCGI connection");

	InvokeIdleDisconnect();
}

/*
 * child_stock class
 *
 */

Event::Duration
FcgiStock::GetChildClearInterval(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;

	return params.options.ns.mount.pivot_root == nullptr
		? std::chrono::minutes(10)
		/* lower clear_interval for jailed (per-account?)
		   processes */
		: std::chrono::minutes(5);
}

bool
FcgiStock::WantReturnStderr(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;
	/* we need the child process to return the stderr_fd to us if
	   the given path is "jailed" */
	return params.options.stderr_path != nullptr &&
		params.options.stderr_jailed;
}

bool
FcgiStock::WantStderrPond(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;
	return params.options.stderr_pond;
}

StringView
FcgiStock::GetChildTag(void *info) const noexcept
{
	const auto &params = *(const FcgiChildParams *)info;

	return params.options.tag;
}

void
FcgiStock::PrepareChild(void *info, UniqueSocketDescriptor &&fd,
			PreparedChildProcess &p)
{
	auto &params = *(FcgiChildParams *)info;
	const ChildOptions &options = params.options;

	p.SetStdin(std::move(fd));

	/* the FastCGI protocol defines a channel for stderr, so we could
	   close its "real" stderr here, but many FastCGI applications
	   don't use the FastCGI protocol to send error messages, so we
	   just keep it open */

	UniqueFileDescriptor null_fd;
	if (null_fd.Open("/dev/null", O_WRONLY))
		p.SetStdout(std::move(null_fd));

	p.Append(params.executable_path);
	for (auto i : params.args)
		p.Append(i);

	options.CopyTo(p);
}

/*
 * stock class
 *
 */

void
FcgiStock::Create(CreateStockItem c, StockRequest request,
		  [[maybe_unused]] CancellablePointer &cancel_ptr)
{
	[[maybe_unused]] auto &params = *(FcgiChildParams *)request.get();
	assert(params.executable_path != nullptr);

	auto *connection = new FcgiConnection(GetEventLoop(), c);

	const char *key = c.GetStockName();

	try {
		connection->child = (ChildStockItem *)
			child_stock.GetStockMap().GetNow(key,
							 std::move(request));
	} catch (...) {
		delete connection;
		std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to start FastCGI server '%s'",
									 key)));
	}

	try {
		connection->fd = connection->child->Connect();
	} catch (...) {
		connection->kill = true;
		delete connection;
		std::throw_with_nested(FcgiClientError(StringFormat<256>("Failed to connect to FastCGI server '%s'",
									 key)));
	}

	connection->event.Open(connection->fd);

	connection->InvokeCreateSuccess();
}

bool
FcgiConnection::Borrow() noexcept
{
	/* check the connection status before using it, just in case the
	   FastCGI server has decided to close the connection before
	   fcgi_connection_event_callback() got invoked */
	char buffer;
	ssize_t nbytes = fd.Read(&buffer, sizeof(buffer));
	if (nbytes > 0) {
		logger(2, "unexpected data from idle FastCGI connection");
		return false;
	} else if (nbytes == 0) {
		/* connection closed (not worth a log message) */
		return false;
	} else if (errno != EAGAIN) {
		logger(2, "error on idle FastCGI connection: ", strerror(errno));
		return false;
	}

	event.Cancel();
	aborted = false;
	return true;
}

bool
FcgiConnection::Release() noexcept
{
	fresh = false;
	event.ScheduleRead();
	return true;
}

FcgiConnection::~FcgiConnection() noexcept
{
	if (fd.IsDefined()) {
		event.Cancel();
		fd.Close();
	}

	if (fresh && aborted)
		/* the fcgi_client caller has aborted the request before the
		   first response on a fresh connection was received: better
		   kill the child process, it may be failing on us
		   completely */
		kill = true;

	if (child != nullptr)
		child->Put(kill);
}

/*
 * interface
 *
 */

inline
FcgiStock::FcgiStock(unsigned limit, unsigned max_idle,
		     EventLoop &event_loop, SpawnService &spawn_service,
		     SocketDescriptor _log_socket,
		     const ChildErrorLogOptions &_log_options) noexcept
	:hstock(event_loop, *this, limit, max_idle,
		std::chrono::minutes(2)),
	 child_stock(event_loop, spawn_service,
		     *this,
		     4,
		     _log_socket, _log_options,
		     limit, max_idle) {}

void
FcgiStock::FadeTag(StringView tag) noexcept
{
	assert(tag != nullptr);

	hstock.FadeIf([tag](const StockItem &item){
		const auto &connection = (const FcgiConnection &)item;
		return StringListContains(connection.GetTag(), '\0', tag);
	});

	child_stock.FadeTag(tag);
}

FcgiStock *
fcgi_stock_new(unsigned limit, unsigned max_idle,
	       EventLoop &event_loop, SpawnService &spawn_service,
	       SocketDescriptor log_socket,
	       const ChildErrorLogOptions &log_options) noexcept
{
	return new FcgiStock(limit, max_idle, event_loop, spawn_service,
			     log_socket, log_options);
}

void
fcgi_stock_free(FcgiStock *fcgi_stock) noexcept
{
	delete fcgi_stock;
}

SocketDescriptor
fcgi_stock_get_log_socket(const FcgiStock &fs) noexcept
{
	return fs.GetLogSocket();
}

const ChildErrorLogOptions &
fcgi_stock_get_log_options(const FcgiStock &fs) noexcept
{
	return fs.GetLogOptions();
}

void
fcgi_stock_fade_all(FcgiStock &fs) noexcept
{
	fs.FadeAll();
}

void
fcgi_stock_fade_tag(FcgiStock &fs, StringView tag) noexcept
{
	fs.FadeTag(tag);
}

inline StockItem *
FcgiStock::Get(const ChildOptions &options,
	       const char *executable_path,
	       ConstBuffer<const char *> args)
{
	const TempPoolLease tpool;

	auto r = NewDisposablePointer<FcgiChildParams>(*tpool, executable_path,
						       args, options);
	const char *key = r->GetStockKey(*tpool);
	return hstock.GetNow(key, std::move(r));
}

StockItem *
fcgi_stock_get(FcgiStock *fcgi_stock,
	       const ChildOptions &options,
	       const char *executable_path,
	       ConstBuffer<const char *> args)
{
	return fcgi_stock->Get(options, executable_path, args);
}

int
fcgi_stock_item_get_domain(gcc_unused const StockItem &item) noexcept
{
	return AF_LOCAL;
}

UniqueFileDescriptor
fcgi_stock_item_get_stderr(const StockItem &item) noexcept
{
	const auto &connection = (const FcgiConnection &)item;
	return connection.GetStderr();
}

void
fcgi_stock_item_set_site(StockItem &item, const char *site) noexcept
{
	auto &connection = (FcgiConnection &)item;
	connection.SetSite(site);
}

void
fcgi_stock_item_set_uri(StockItem &item, const char *uri) noexcept
{
	auto &connection = (FcgiConnection &)item;
	connection.SetUri(uri);
}

SocketDescriptor
fcgi_stock_item_get(const StockItem &item) noexcept
{
	const auto *connection = (const FcgiConnection *)&item;

	assert(connection->fd.IsDefined());

	return connection->fd;
}

void
fcgi_stock_aborted(StockItem &item) noexcept
{
	auto *connection = (FcgiConnection *)&item;

	connection->aborted = true;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2015 John Connor (BM-NC49AxAjcqVcF5jNPu85Rb8MJ2d9JqZt)
 *
 * This file is part of vanillacoin.
 *
 * vanillacoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>
#include <cstdlib>

#include <boost/asio.hpp>
#include <boost/format.hpp>

#include <coin/constants.hpp>
#include <coin/filesystem.hpp>
#include <coin/logger.hpp>

using namespace coin;

#if (defined _MSC_VER)
#include "Shlobj.h"
#define ERRNO GetLastError()
static int _mkdir(const char * path)
{
    std::wstring directory(path, path + strlen(path));

    return SHCreateDirectoryEx(NULL, directory.c_str(), NULL );
}
#define CREATE_DIRECTORY(P) _mkdir(P)
#else
#include <sys/stat.h>
#define ERRNO errno
#define ERROR_ALREADY_EXISTS EEXIST
static int _mkdir(const char * dir)
{
    char tmp[256];
    char * p = NULL;
    size_t len;

    snprintf(tmp, sizeof(tmp),"%s",dir);
    len = strlen(tmp);

    if (tmp[len - 1] == '/')
    {
        tmp[len - 1] = 0;
    }

    for (p = tmp + 1; *p; p++)
    {
        if (*p == '/')
        {
            *p = 0;

            mkdir(tmp, S_IRWXU);

            *p = '/';
        }
    }

    return mkdir(tmp, S_IRWXU);
}
#define CREATE_DIRECTORY(P) _mkdir(P)
#endif

int filesystem::error_already_exists = ERROR_ALREADY_EXISTS;

int filesystem::create_path(const std::string & path)
{
    if (CREATE_DIRECTORY(path.c_str()) == 0)
    {
        return 0;
    }

    return ERRNO;
}

bool filesystem::copy_file(const std::string & src, const std::string & dest)
{
    enum { buffer_length = 32768 };

    std::vector<char> buf(buffer_length);

    auto infile =- 1, outfile =- 1;

    if ((infile = ::open(src.c_str(), O_RDONLY)) < 0)
    {
        return false;
    }

    struct stat from_stat;

    if (::stat(src.c_str(), &from_stat)!= 0)
    {
        ::close(infile);

        return false;
    }

    int oflag = O_CREAT | O_WRONLY | O_TRUNC;

    if ((outfile = ::open(dest.c_str(), oflag, from_stat.st_mode)) < 0)
    {
        int open_errno = errno;

        assert(infile >= 0);

        ::close(infile);

        errno = open_errno;

        return false;
    }

    ssize_t sz, read = 1, write;

    while (
        read > 0 && (read = ::read(infile, &buf[0], buffer_length)) > 0
        )
    {
        write = 0;

        do
        {
            if (
                (sz = ::write(outfile, &buf[0] + write, read - write)) < 0
                )
            {
                read = sz;

                break;
            }

            write += sz;

        } while (write < read);
    }

    if (::close(infile) < 0)
    {
        read = -1;
    }

    if (::close(outfile) < 0)
    {
        read = -1;
    }

    return read >= 0;
}

std::string filesystem::data_path()
{
    static const std::string bundle_id = constants::client_name;
    std::string ret;
#if (defined _MSC_VER)
    ret += getenv("APPDATA");
    ret += "\\" + bundle_id + "\\";
#elif (defined __APPLE__)
    ret = home_path();
    ret += "Library/";
    ret += "Application Support/";
    ret += bundle_id + "/";
#elif (defined __ANDROID__)
    ret = home_path() + bundle_id + "/";
#else
    ret = home_path();
    ret += "." + bundle_id + "/data/";
#endif
    return ret;
}

std::string filesystem::home_path()
{
    std::string ret;
#if (defined __ANDROID__)
    static const std::string android_package = constants::client_name;
    ret = "/data/data/" + android_package;
#else
    if (std::getenv("HOME"))
    {
        ret = std::getenv("HOME");
    }
    else if (std::getenv("USERPOFILE"))
    {
        ret = std::getenv("USERPOFILE");
    }
    else if (std::getenv("HOMEDRIVE") && std::getenv("HOMEPATH"))
    {
        ret = (
            boost::format("%1%%2%") % std::getenv("HOMEDRIVE") %
            std::getenv("HOMEPATH")
        ).str();
    }
    else
    {
        ret = ".";
    }
#endif // __ANDROID__
#if (defined _MSC_VER)
    return ret + "\\";
#else
    return ret + "/";
#endif
}
<commit_msg>copy file faster using native c++<commit_after>/*
 * Copyright (c) 2013-2015 John Connor (BM-NC49AxAjcqVcF5jNPu85Rb8MJ2d9JqZt)
 *
 * This file is part of vanillacoin.
 *
 * vanillacoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>
#include <cstdio>
#include <fstream>

#include <boost/asio.hpp>
#include <boost/format.hpp>

#include <coin/constants.hpp>
#include <coin/filesystem.hpp>
#include <coin/logger.hpp>

using namespace coin;

#if (defined _MSC_VER)
#include "Shlobj.h"
#define ERRNO GetLastError()
static int _mkdir(const char * path)
{
    std::wstring directory(path, path + strlen(path));

    return SHCreateDirectoryEx(NULL, directory.c_str(), NULL );
}
#define CREATE_DIRECTORY(P) _mkdir(P)
#else
#include <sys/stat.h>
#define ERRNO errno
#define ERROR_ALREADY_EXISTS EEXIST
static int _mkdir(const char * dir)
{
    char tmp[256];
    char * p = NULL;
    size_t len;

    snprintf(tmp, sizeof(tmp),"%s",dir);
    len = strlen(tmp);

    if (tmp[len - 1] == '/')
    {
        tmp[len - 1] = 0;
    }

    for (p = tmp + 1; *p; p++)
    {
        if (*p == '/')
        {
            *p = 0;

            mkdir(tmp, S_IRWXU);

            *p = '/';
        }
    }

    return mkdir(tmp, S_IRWXU);
}
#define CREATE_DIRECTORY(P) _mkdir(P)
#endif

int filesystem::error_already_exists = ERROR_ALREADY_EXISTS;

int filesystem::create_path(const std::string & path)
{
    if (CREATE_DIRECTORY(path.c_str()) == 0)
    {
        return 0;
    }

    return ERRNO;
}

bool filesystem::copy_file(const std::string & src, const std::string & dest)
{
    try
    {
        std::ifstream ifs(src, std::ios::binary);

        std::ofstream ofs(dest, std::ios::binary);

        ofs << ifs.rdbuf();

        ifs.close();

        ofs.close();
    }
    catch (...)
    {
        return false;
    }

    return true;
}

std::string filesystem::data_path()
{
    static const std::string bundle_id = constants::client_name;
    std::string ret;
#if (defined _MSC_VER)
    ret += getenv("APPDATA");
    ret += "\\" + bundle_id + "\\";
#elif (defined __APPLE__)
    ret = home_path();
    ret += "Library/";
    ret += "Application Support/";
    ret += bundle_id + "/";
#elif (defined __ANDROID__)
    ret = home_path() + bundle_id + "/";
#else
    ret = home_path();
    ret += "." + bundle_id + "/data/";
#endif
    return ret;
}

std::string filesystem::home_path()
{
    std::string ret;
#if (defined __ANDROID__)
    static const std::string android_package = constants::client_name;
    ret = "/data/data/" + android_package;
#else
    if (std::getenv("HOME"))
    {
        ret = std::getenv("HOME");
    }
    else if (std::getenv("USERPOFILE"))
    {
        ret = std::getenv("USERPOFILE");
    }
    else if (std::getenv("HOMEDRIVE") && std::getenv("HOMEPATH"))
    {
        ret = (
            boost::format("%1%%2%") % std::getenv("HOMEDRIVE") %
            std::getenv("HOMEPATH")
        ).str();
    }
    else
    {
        ret = ".";
    }
#endif // __ANDROID__
#if (defined _MSC_VER)
    return ret + "\\";
#else
    return ret + "/";
#endif
}
<|endoftext|>


<commit_before>#include <matrix.hpp>

	Matrix :: Matrix(){};

	Matrix :: Matrix(int a, int b)
	{
		columns = b;
		strings = a;
		matrix = new int*[a];

		for (int i = 0; i < a; i++)
		{
			matrix[i] = new int[b]{0};
		}
	};

	 void Matrix :: print(void)
	{
		for (int i = 0; i < strings; i++)
		{
			for (int j = 0; j < columns; j++)
			{
				cout << get(i, j) << " ";
			}
			cout << endl;
		}
		cout << endl;
	};

	void Matrix :: input(string filen)
	{
		ifstream file;
		file.open(filen);

		matrix[i] = new *int [strings];
		for (int i = 0; i < strings; i++)
		{
			matrix[i] = new *int [columns];
			for (int j = 0; j < columns; j++)
			{
				file >> matrix[i][j];
			}
		}
		file.close();
	};

	void Matrix :: set(int x, int y, int z)
	{
		matrix[x][y] = z;
	};

	int Matrix :: get(int x, int y) const
	{
		return matrix[x][y];
	};

	Matrix Matrix:: operator+ (Matrix a) const
	{
		Matrix c(strings, columns);

		for (int i = 0; i < strings; i++)
		{
			for (int j = 0; j < columns; j++)
			{
				c.set(i, j, a.get(i, j) + get(i, j));
			}
		}
		return c;
	};

	Matrix Matrix :: operator* (Matrix a) const
	{
		Matrix c(strings, a.columns);

		for (int i = 0; i < strings; i++)
		{
			for (int j = 0; j < a.columns; j++)
			{
				int temp = 0;
				for (int k = 0; k < strings; k++)
				{
					temp += get(i, k) * a.get(k, j);
				}
				c.set(i, j, temp);
			}
		}

		return c;
	};

	Matrix& Matrix :: operator= (Matrix &other)
	{
		if (this != &other)
		{
			for (int i = 0; i < strings; i++)
			{
				delete[] matrix[i];
			}

			delete[] matrix;

			columns = other.columns;
			strings = other.strings;

			matrix = new int*[strings];

			for (int i = 0; i < strings; i++)
			{
				matrix[i] = new int[columns]{0};
			}

			matrix = other.matrix;
		}
		return *this;
	};

	bool Matrix :: operator== (Matrix &a) const
	{
		bool k = false;

		for (int i = 0; i < strings; i++){

			for (int j = 0; j < columns; j++){

				if (matrix[i][j] == a.matrix[i][j])

					k = true;

			}

		}

		return k;
	}

	istream& operator>> (istream& file, const Matrix& result)

	{

		for (int i = 0; i < result.strings; i++)

			for (int j = 0; j < result.columns; j++)

				file >> result.matrix[i][j];

		return file;

	}

	 ostream& operator<< (ostream& os, const Matrix& a)
	{
		for (int i = 0; i < a.strings; i++)
		{
			for (int j = 0; j < a.columns; j++)
			{
				os << a.matrix[i, j] << " ";
			}
			os << endl;
		}
		os << endl;
		return os;
	}

	Matrix :: ~Matrix()
	{
		/*for (int i = 0; i < strings; i++)
		{
			delete[] matrix[i];
		}

		delete[] matrix;*/
	};
};

<commit_msg>Update matrix.cpp<commit_after>#include <matrix.hpp>

	Matrix :: Matrix(){};

	Matrix :: Matrix(int a, int b)
	{
		columns = b;
		strings = a;
		matrix = new int*[a];

		for (int i = 0; i < a; i++)
		{
			matrix[i] = new int[b]{0};
		}
	};

	void Matrix :: input(string filen)
	{
		ifstream file;
		file.open(filen);

		matrix[i] = new *int [strings];
		for (int i = 0; i < strings; i++)
		{
			matrix[i] = new *int [columns];
			for (int j = 0; j < columns; j++)
			{
				file >> matrix[i][j];
			}
		}
		file.close();
	};

	void Matrix :: set(int x, int y, int z)
	{
		matrix[x][y] = z;
	};

	int Matrix :: get(int x, int y) const
	{
		return matrix[x][y];
	};

	Matrix Matrix:: operator+ (Matrix a) const
	{
		Matrix c(strings, columns);

		for (int i = 0; i < strings; i++)
		{
			for (int j = 0; j < columns; j++)
			{
				c.set(i, j, a.get(i, j) + get(i, j));
			}
		}
		return c;
	};

	Matrix Matrix :: operator* (Matrix a) const
	{
		Matrix c(strings, a.columns);

		for (int i = 0; i < strings; i++)
		{
			for (int j = 0; j < a.columns; j++)
			{
				int temp = 0;
				for (int k = 0; k < strings; k++)
				{
					temp += get(i, k) * a.get(k, j);
				}
				c.set(i, j, temp);
			}
		}

		return c;
	};

	Matrix& Matrix :: operator= (Matrix &other)
	{
		if (this != &other)
		{
			for (int i = 0; i < strings; i++)
			{
				delete[] matrix[i];
			}

			delete[] matrix;

			columns = other.columns;
			strings = other.strings;

			matrix = new int*[strings];

			for (int i = 0; i < strings; i++)
			{
				matrix[i] = new int[columns]{0};
			}

			matrix = other.matrix;
		}
		return *this;
	};

	bool Matrix :: operator== (Matrix &a) const
	{
		bool k = false;

		for (int i = 0; i < strings; i++){

			for (int j = 0; j < columns; j++){

				if (matrix[i][j] == a.matrix[i][j])

					k = true;

			}

		}

		return k;
	}

	istream& operator>> (istream& file, const Matrix& result)

	{

		for (int i = 0; i < result.strings; i++)

			for (int j = 0; j < result.columns; j++)

				file >> result.matrix[i][j];

		return file;

	}

	 ostream& operator<< (ostream& os, const Matrix& a)
	{
		for (int i = 0; i < a.strings; i++)
		{
			for (int j = 0; j < a.columns; j++)
			{
				os << a.matrix[i, j] << " ";
			}
			os << endl;
		}
		os << endl;
		return os;
	}

	Matrix :: ~Matrix()
	{
		/*for (int i = 0; i < strings; i++)
		{
			delete[] matrix[i];
		}

		delete[] matrix;*/
	};
};

<|endoftext|>


<commit_before>#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>

#include <fuse.h>

using namespace std;

static const char image_path[] = "/sparsebundle.dmg";

struct sparsebundle_data {
    char *path;
    off_t band_size;
    off_t size;
};

#define SB_DATA_CAST(ptr) ((struct sparsebundle_data *) ptr)
#define SB_DATA (SB_DATA_CAST(fuse_get_context()->private_data))

static int sparsebundle_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
{
    if (strcmp(path, "/") != 0)
        return -ENOENT;

    filler(buf, ".", 0, 0);
    filler(buf, "..", 0, 0);
    filler(buf, image_path + 1, 0, 0);

    return 0;
}

static int sparsebundle_getattr(const char *path, struct stat *stbuf)
{
    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 3;
    } else if (strcmp(path, image_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = SB_DATA->size;
    } else
        return -ENOENT;

    return 0;
}

static int sparsebundle_open(const char *path, struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if ((fi->flags & O_ACCMODE) != O_RDONLY)
        return -EACCES;

    return 0;
}

static int sparsebundle_read(const char *path, char *buffer, size_t length, off_t offset,
           struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if (offset >= SB_DATA->size)
        return 0;

    if (offset + length > SB_DATA->size)
        length = SB_DATA->size - offset;

    syslog(LOG_DEBUG, "asked to read %zu bytes at offset %llu", length, offset);

    size_t bytes_read = 0;
    while (bytes_read < length) {
        off_t band_number = (offset + bytes_read) / SB_DATA->band_size;
        off_t band_offset = (offset + bytes_read) % SB_DATA->band_size;

        ssize_t to_read = min(static_cast<off_t>(length - bytes_read),
            SB_DATA->band_size - band_offset);

        char *band_name;
        if (asprintf(&band_name, "%s/bands/%llx", SB_DATA->path, band_number) == -1) {
            syslog(LOG_ERR, "failed to resolve band name");
            return -1;
        }

        syslog(LOG_DEBUG, "reading %zu bytes from band %llx at offset %llu",
            to_read, band_number, band_offset);

        ssize_t read = 0;
        int band_file = open(band_name, O_RDONLY);
        if (band_file != -1) {
            read = pread(band_file, buffer + bytes_read, to_read, band_offset);
            close(band_file);

            if (read == -1) {
                syslog(LOG_ERR, "failed to read band: %s", strerror(errno));
                free(band_name);
                return -1;
            }
        } else if (errno != ENOENT) {
            syslog(LOG_ERR, "failed to open band %s: %s", band_name, strerror(errno));
            free(band_name);
            return -1;
        }

        free(band_name);

        if (read < to_read) {
            syslog(LOG_DEBUG, "missing %zu bytes from band %llx, padding with zeroes",
                to_read - read, band_number);
            memset(buffer + bytes_read + read, 0, to_read - read);
        }

        bytes_read += to_read;

        syslog(LOG_DEBUG, "done reading from band %llx, %zu bytes left to read",
                band_number, length - bytes_read);
    }

    assert(bytes_read == length);
    return bytes_read;
}

static int sparsebundle_show_usage(char *program_name)
{
    fprintf(stderr, "usage: %s [-o options] [-f] [-D] <sparsebundle> <mountpoint>\n", program_name);
    return 1;
}

enum { SPARSEBUNDLE_OPT_DEBUG };

static int sparsebundle_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
    switch (key) {
    case SPARSEBUNDLE_OPT_DEBUG:
        setlogmask(LOG_UPTO(LOG_DEBUG));
        return 0;
    case FUSE_OPT_KEY_NONOPT:
        if (SB_DATA_CAST(data)->path)
            return 1;

        SB_DATA_CAST(data)->path = strdup(arg);
        return 0;
    }

    return 1;
}

static off_t read_size(const string &str)
{
    uintmax_t value = strtoumax(str.c_str(), 0, 10);
    if (errno == ERANGE || value > static_cast<uintmax_t>(numeric_limits<off_t>::max())) {
        fprintf(stderr, "Disk image too large to be mounted (%s bytes)\n", str.c_str());
        exit(-1);
    }

    return value;
}

int main(int argc, char **argv)
{
    openlog("sparsebundlefs", LOG_CONS | LOG_PERROR, LOG_USER);
    setlogmask(~(LOG_MASK(LOG_DEBUG)));

    struct sparsebundle_data data = {};

    static struct fuse_opt sparsebundle_options[] = {
        FUSE_OPT_KEY("-D",  SPARSEBUNDLE_OPT_DEBUG), FUSE_OPT_END
    };

    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    fuse_opt_parse(&args, &data, sparsebundle_options, sparsebundle_opt_proc);
    fuse_opt_add_arg(&args, "-oro"); // Force read-only mount

    if (!data.path)
        return sparsebundle_show_usage(argv[0]);

    char *abs_path = realpath(data.path, 0);
    if (!abs_path) {
        perror("Could not resolve absolute path");
        return -1;
    }

    free(data.path);
    data.path = abs_path;

    char *plist_path;
    if (asprintf(&plist_path, "%s/Info.plist", data.path) == -1) {
        perror("Failed to resolve Info.plist path");
        return -1;
    }

    ifstream plist_file(plist_path);
    stringstream plist_data;
    plist_data << plist_file.rdbuf();

    string key, line;
    while (getline(plist_data, line)) {
        static const char whitespace_chars[] = " \n\r\t";
        line.erase(0, line.find_first_not_of(whitespace_chars));
        line.erase(line.find_last_not_of(whitespace_chars) + 1);

        if (line.compare(0, 5, "<key>") == 0) {
            key = line.substr(5, line.length() - 11);
        } else if (!key.empty()) {
            line.erase(0, line.find_first_of('>') + 1);
            line.erase(line.find_first_of('<'));

            if (key == "band-size")
                data.band_size = read_size(line);
            else if (key == "size")
                data.size = read_size(line);

            key.clear();
        }
    }

    struct fuse_operations sparsebundle_filesystem_operations = {};
    sparsebundle_filesystem_operations.getattr = sparsebundle_getattr;
    sparsebundle_filesystem_operations.open = sparsebundle_open;
    sparsebundle_filesystem_operations.read = sparsebundle_read;
    sparsebundle_filesystem_operations.readdir = sparsebundle_readdir;

    return fuse_main(args.argc, args.argv, &sparsebundle_filesystem_operations, &data);
}
<commit_msg>Implement release callback<commit_after>#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>

#include <fuse.h>

using namespace std;

static const char image_path[] = "/sparsebundle.dmg";

struct sparsebundle_data {
    char *path;
    off_t band_size;
    off_t size;
    off_t times_opened;
};

#define SB_DATA_CAST(ptr) ((struct sparsebundle_data *) ptr)
#define SB_DATA (SB_DATA_CAST(fuse_get_context()->private_data))

static int sparsebundle_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
{
    if (strcmp(path, "/") != 0)
        return -ENOENT;

    filler(buf, ".", 0, 0);
    filler(buf, "..", 0, 0);
    filler(buf, image_path + 1, 0, 0);

    return 0;
}

static int sparsebundle_getattr(const char *path, struct stat *stbuf)
{
    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 3;
    } else if (strcmp(path, image_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = SB_DATA->size;
    } else
        return -ENOENT;

    return 0;
}

static int sparsebundle_open(const char *path, struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if ((fi->flags & O_ACCMODE) != O_RDONLY)
        return -EACCES;

    SB_DATA->times_opened++;
    syslog(LOG_DEBUG, "opened %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    return 0;
}

static int sparsebundle_read(const char *path, char *buffer, size_t length, off_t offset,
           struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if (offset >= SB_DATA->size)
        return 0;

    if (offset + length > SB_DATA->size)
        length = SB_DATA->size - offset;

    syslog(LOG_DEBUG, "asked to read %zu bytes at offset %llu", length, offset);

    size_t bytes_read = 0;
    while (bytes_read < length) {
        off_t band_number = (offset + bytes_read) / SB_DATA->band_size;
        off_t band_offset = (offset + bytes_read) % SB_DATA->band_size;

        ssize_t to_read = min(static_cast<off_t>(length - bytes_read),
            SB_DATA->band_size - band_offset);

        char *band_name;
        if (asprintf(&band_name, "%s/bands/%llx", SB_DATA->path, band_number) == -1) {
            syslog(LOG_ERR, "failed to resolve band name");
            return -1;
        }

        syslog(LOG_DEBUG, "reading %zu bytes from band %llx at offset %llu",
            to_read, band_number, band_offset);

        ssize_t read = 0;
        int band_file = open(band_name, O_RDONLY);
        if (band_file != -1) {
            read = pread(band_file, buffer + bytes_read, to_read, band_offset);
            close(band_file);

            if (read == -1) {
                syslog(LOG_ERR, "failed to read band: %s", strerror(errno));
                free(band_name);
                return -1;
            }
        } else if (errno != ENOENT) {
            syslog(LOG_ERR, "failed to open band %s: %s", band_name, strerror(errno));
            free(band_name);
            return -1;
        }

        free(band_name);

        if (read < to_read) {
            syslog(LOG_DEBUG, "missing %zu bytes from band %llx, padding with zeroes",
                to_read - read, band_number);
            memset(buffer + bytes_read + read, 0, to_read - read);
        }

        bytes_read += to_read;

        syslog(LOG_DEBUG, "done reading from band %llx, %zu bytes left to read",
                band_number, length - bytes_read);
    }

    assert(bytes_read == length);
    return bytes_read;
}

static int sparsebundle_release(const char *path, struct fuse_file_info *fi)
{
    SB_DATA->times_opened--;
    syslog(LOG_DEBUG, "closed %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    if (SB_DATA->times_opened == 0) {
        syslog(LOG_DEBUG, "no more references to sparsebundle, cleaning up");
    }

    return 0;
}

static int sparsebundle_show_usage(char *program_name)
{
    fprintf(stderr, "usage: %s [-o options] [-f] [-D] <sparsebundle> <mountpoint>\n", program_name);
    return 1;
}

enum { SPARSEBUNDLE_OPT_DEBUG };

static int sparsebundle_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
    switch (key) {
    case SPARSEBUNDLE_OPT_DEBUG:
        setlogmask(LOG_UPTO(LOG_DEBUG));
        return 0;
    case FUSE_OPT_KEY_NONOPT:
        if (SB_DATA_CAST(data)->path)
            return 1;

        SB_DATA_CAST(data)->path = strdup(arg);
        return 0;
    }

    return 1;
}

static off_t read_size(const string &str)
{
    uintmax_t value = strtoumax(str.c_str(), 0, 10);
    if (errno == ERANGE || value > static_cast<uintmax_t>(numeric_limits<off_t>::max())) {
        fprintf(stderr, "Disk image too large to be mounted (%s bytes)\n", str.c_str());
        exit(-1);
    }

    return value;
}

int main(int argc, char **argv)
{
    openlog("sparsebundlefs", LOG_CONS | LOG_PERROR, LOG_USER);
    setlogmask(~(LOG_MASK(LOG_DEBUG)));

    struct sparsebundle_data data = {};

    static struct fuse_opt sparsebundle_options[] = {
        FUSE_OPT_KEY("-D",  SPARSEBUNDLE_OPT_DEBUG), FUSE_OPT_END
    };

    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    fuse_opt_parse(&args, &data, sparsebundle_options, sparsebundle_opt_proc);
    fuse_opt_add_arg(&args, "-oro"); // Force read-only mount

    if (!data.path)
        return sparsebundle_show_usage(argv[0]);

    char *abs_path = realpath(data.path, 0);
    if (!abs_path) {
        perror("Could not resolve absolute path");
        return -1;
    }

    free(data.path);
    data.path = abs_path;

    char *plist_path;
    if (asprintf(&plist_path, "%s/Info.plist", data.path) == -1) {
        perror("Failed to resolve Info.plist path");
        return -1;
    }

    ifstream plist_file(plist_path);
    stringstream plist_data;
    plist_data << plist_file.rdbuf();

    string key, line;
    while (getline(plist_data, line)) {
        static const char whitespace_chars[] = " \n\r\t";
        line.erase(0, line.find_first_not_of(whitespace_chars));
        line.erase(line.find_last_not_of(whitespace_chars) + 1);

        if (line.compare(0, 5, "<key>") == 0) {
            key = line.substr(5, line.length() - 11);
        } else if (!key.empty()) {
            line.erase(0, line.find_first_of('>') + 1);
            line.erase(line.find_first_of('<'));

            if (key == "band-size")
                data.band_size = read_size(line);
            else if (key == "size")
                data.size = read_size(line);

            key.clear();
        }
    }

    struct fuse_operations sparsebundle_filesystem_operations = {};
    sparsebundle_filesystem_operations.getattr = sparsebundle_getattr;
    sparsebundle_filesystem_operations.open = sparsebundle_open;
    sparsebundle_filesystem_operations.read = sparsebundle_read;
    sparsebundle_filesystem_operations.readdir = sparsebundle_readdir;
    sparsebundle_filesystem_operations.release = sparsebundle_release;

    return fuse_main(args.argc, args.argv, &sparsebundle_filesystem_operations, &data);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Connect.hxx"
#include "FilteredSocket.hxx"
#include "Factory.hxx"
#include "event/net/ConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "system/Error.hxx"
#include "stopwatch.hxx"

#include <exception>

#include <sys/socket.h>
#include <netinet/in.h>

class ConnectFilteredSocketOperation final
	: Cancellable, ConnectSocketHandler, BufferedSocketHandler
{
	ConnectFilteredSocketHandler &handler;

	const StopwatchPtr stopwatch;

	TimerEvent timeout_event;

	ConnectSocket connect_socket;

	SocketFilterFactory *const filter_factory;

	std::unique_ptr<FilteredSocket> socket;

	FdType fd_type;

public:
	ConnectFilteredSocketOperation(EventLoop &event_loop,
				       SocketFilterFactory *_filter_factory,
				       StopwatchPtr &&_stopwatch,
				       ConnectFilteredSocketHandler &_handler,
				       CancellablePointer &caller_cancel_ptr) noexcept
		:handler(_handler), stopwatch(std::move(_stopwatch)),
		 timeout_event(event_loop, BIND_THIS_METHOD(OnTimeout)),
		 connect_socket(event_loop, *this),
		 filter_factory(_filter_factory)
	{
		caller_cancel_ptr = *this;
	}

	auto &GetEventLoop() const noexcept {
		return connect_socket.GetEventLoop();
	}

	void Start(bool ip_transparent,
		   SocketAddress bind_address,
		   SocketAddress address,
		   Event::Duration timeout) noexcept;

private:
	void OnHandshake() noexcept;
	void OnTimeout() noexcept;

	/* virtual methods from class Cancellable */
	void Cancel() noexcept override {
		if (socket)
			socket->Close();
		else
			connect_socket.Cancel();

		delete this;
	}

	/* virtual methods from class ConnectSocketHandler */
	void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept override;

	void OnSocketConnectError(std::exception_ptr e) noexcept override {
		stopwatch.RecordEvent("error");
		handler.OnConnectFilteredSocketError(std::move(e));
		delete this;
	}

	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;

	gcc_noreturn
	bool OnBufferedWrite() override {
		/* should never be reached because we never schedule
		   writing */
		gcc_unreachable();
	}

	void OnBufferedError(std::exception_ptr e) noexcept override;
};

void
ConnectFilteredSocketOperation::Start(bool ip_transparent,
				      SocketAddress bind_address,
				      SocketAddress address,
				      Event::Duration timeout) noexcept
try {
	const int address_family = address.GetFamily();
	fd_type = address_family == AF_LOCAL
		? FD_SOCKET
		: FD_TCP;

	UniqueSocketDescriptor fd;

	if (!fd.CreateNonBlock(address_family, SOCK_STREAM, 0))
		throw MakeErrno("Failed to create socket");

	if ((address_family == PF_INET || address_family == PF_INET6) &&
	    !fd.SetNoDelay())
		throw MakeErrno("Failed to set TCP_NODELAY");

	if (ip_transparent && !fd.SetBoolOption(SOL_IP, IP_TRANSPARENT, true))
		throw MakeErrno("Failed to set IP_TRANSPARENT");

	if (!bind_address.IsNull() && bind_address.IsDefined() &&
	    !fd.Bind(bind_address))
		throw MakeErrno("Failed to bind socket");

	if (fd.Connect(address)) {
		OnSocketConnectSuccess(std::move(fd));
		return;
	}

	if (errno != EINPROGRESS)
		throw MakeErrno("Failed to connect");

	connect_socket.WaitConnected(std::move(fd), Event::Duration(-1));
	timeout_event.Schedule(timeout);
} catch (...) {
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::current_exception());
	delete this;
}

void
ConnectFilteredSocketOperation::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept
{
	stopwatch.RecordEvent("connect");

	socket = std::make_unique<FilteredSocket>(GetEventLoop());

	try {
		socket->Init(fd.Release(), fd_type,
			     Event::Duration(-1), Event::Duration(-1),
			     filter_factory != nullptr
			     ? filter_factory->CreateFilter()
			     : nullptr,
			     *this);
	} catch (...) {
		stopwatch.RecordEvent("error");
		handler.OnConnectFilteredSocketError(std::current_exception());
		delete this;
	}

	if (filter_factory) {
		socket->ScheduleWrite();
		socket->SetHandshakeCallback(BIND_THIS_METHOD(OnHandshake));
	} else {
		handler.OnConnectFilteredSocket(std::move(socket));
	}
}

BufferedResult
ConnectFilteredSocketOperation::OnBufferedData()
{
	return BufferedResult::BLOCKING;
}

bool
ConnectFilteredSocketOperation::OnBufferedClosed() noexcept
{
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::make_exception_ptr(std::runtime_error("Peer closed the connection prematurely")));
	delete this;
	return false;
}

void
ConnectFilteredSocketOperation::OnBufferedError(std::exception_ptr e) noexcept
{
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::move(e));
	delete this;
}

void
ConnectFilteredSocketOperation::OnHandshake() noexcept
{
	assert(socket);
	assert(socket->IsConnected());

	stopwatch.RecordEvent("handshake");

	handler.OnConnectFilteredSocket(std::move(socket));
	delete this;
}

void
ConnectFilteredSocketOperation::OnTimeout() noexcept
{
	stopwatch.RecordEvent("timeout");
	handler.OnConnectFilteredSocketError(std::make_exception_ptr(std::runtime_error("Timeout")));
	delete this;
}

void
ConnectFilteredSocket(EventLoop &event_loop,
		      StopwatchPtr stopwatch,
		      bool ip_transparent,
		      SocketAddress bind_address,
		      SocketAddress address,
		      Event::Duration timeout,
		      SocketFilterFactory *filter_factory,
		      ConnectFilteredSocketHandler &handler,
		      CancellablePointer &cancel_ptr) noexcept
{
	auto *cfs = new ConnectFilteredSocketOperation(event_loop,
						       filter_factory,
						       std::move(stopwatch),
						       handler, cancel_ptr);
	cfs->Start(ip_transparent, bind_address, address, timeout);
}
<commit_msg>fs/Connect: use LeakDetector<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Connect.hxx"
#include "FilteredSocket.hxx"
#include "Factory.hxx"
#include "event/net/ConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "system/Error.hxx"
#include "util/LeakDetector.hxx"
#include "stopwatch.hxx"

#include <exception>

#include <sys/socket.h>
#include <netinet/in.h>

class ConnectFilteredSocketOperation final
	: Cancellable, ConnectSocketHandler, BufferedSocketHandler,
	  LeakDetector
{
	ConnectFilteredSocketHandler &handler;

	const StopwatchPtr stopwatch;

	TimerEvent timeout_event;

	ConnectSocket connect_socket;

	SocketFilterFactory *const filter_factory;

	std::unique_ptr<FilteredSocket> socket;

	FdType fd_type;

public:
	ConnectFilteredSocketOperation(EventLoop &event_loop,
				       SocketFilterFactory *_filter_factory,
				       StopwatchPtr &&_stopwatch,
				       ConnectFilteredSocketHandler &_handler,
				       CancellablePointer &caller_cancel_ptr) noexcept
		:handler(_handler), stopwatch(std::move(_stopwatch)),
		 timeout_event(event_loop, BIND_THIS_METHOD(OnTimeout)),
		 connect_socket(event_loop, *this),
		 filter_factory(_filter_factory)
	{
		caller_cancel_ptr = *this;
	}

	auto &GetEventLoop() const noexcept {
		return connect_socket.GetEventLoop();
	}

	void Start(bool ip_transparent,
		   SocketAddress bind_address,
		   SocketAddress address,
		   Event::Duration timeout) noexcept;

private:
	void OnHandshake() noexcept;
	void OnTimeout() noexcept;

	/* virtual methods from class Cancellable */
	void Cancel() noexcept override {
		if (socket)
			socket->Close();
		else
			connect_socket.Cancel();

		delete this;
	}

	/* virtual methods from class ConnectSocketHandler */
	void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept override;

	void OnSocketConnectError(std::exception_ptr e) noexcept override {
		stopwatch.RecordEvent("error");
		handler.OnConnectFilteredSocketError(std::move(e));
		delete this;
	}

	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;

	gcc_noreturn
	bool OnBufferedWrite() override {
		/* should never be reached because we never schedule
		   writing */
		gcc_unreachable();
	}

	void OnBufferedError(std::exception_ptr e) noexcept override;
};

void
ConnectFilteredSocketOperation::Start(bool ip_transparent,
				      SocketAddress bind_address,
				      SocketAddress address,
				      Event::Duration timeout) noexcept
try {
	const int address_family = address.GetFamily();
	fd_type = address_family == AF_LOCAL
		? FD_SOCKET
		: FD_TCP;

	UniqueSocketDescriptor fd;

	if (!fd.CreateNonBlock(address_family, SOCK_STREAM, 0))
		throw MakeErrno("Failed to create socket");

	if ((address_family == PF_INET || address_family == PF_INET6) &&
	    !fd.SetNoDelay())
		throw MakeErrno("Failed to set TCP_NODELAY");

	if (ip_transparent && !fd.SetBoolOption(SOL_IP, IP_TRANSPARENT, true))
		throw MakeErrno("Failed to set IP_TRANSPARENT");

	if (!bind_address.IsNull() && bind_address.IsDefined() &&
	    !fd.Bind(bind_address))
		throw MakeErrno("Failed to bind socket");

	if (fd.Connect(address)) {
		OnSocketConnectSuccess(std::move(fd));
		return;
	}

	if (errno != EINPROGRESS)
		throw MakeErrno("Failed to connect");

	connect_socket.WaitConnected(std::move(fd), Event::Duration(-1));
	timeout_event.Schedule(timeout);
} catch (...) {
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::current_exception());
	delete this;
}

void
ConnectFilteredSocketOperation::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept
{
	stopwatch.RecordEvent("connect");

	socket = std::make_unique<FilteredSocket>(GetEventLoop());

	try {
		socket->Init(fd.Release(), fd_type,
			     Event::Duration(-1), Event::Duration(-1),
			     filter_factory != nullptr
			     ? filter_factory->CreateFilter()
			     : nullptr,
			     *this);
	} catch (...) {
		stopwatch.RecordEvent("error");
		handler.OnConnectFilteredSocketError(std::current_exception());
		delete this;
	}

	if (filter_factory) {
		socket->ScheduleWrite();
		socket->SetHandshakeCallback(BIND_THIS_METHOD(OnHandshake));
	} else {
		handler.OnConnectFilteredSocket(std::move(socket));
	}
}

BufferedResult
ConnectFilteredSocketOperation::OnBufferedData()
{
	return BufferedResult::BLOCKING;
}

bool
ConnectFilteredSocketOperation::OnBufferedClosed() noexcept
{
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::make_exception_ptr(std::runtime_error("Peer closed the connection prematurely")));
	delete this;
	return false;
}

void
ConnectFilteredSocketOperation::OnBufferedError(std::exception_ptr e) noexcept
{
	stopwatch.RecordEvent("error");
	handler.OnConnectFilteredSocketError(std::move(e));
	delete this;
}

void
ConnectFilteredSocketOperation::OnHandshake() noexcept
{
	assert(socket);
	assert(socket->IsConnected());

	stopwatch.RecordEvent("handshake");

	handler.OnConnectFilteredSocket(std::move(socket));
	delete this;
}

void
ConnectFilteredSocketOperation::OnTimeout() noexcept
{
	stopwatch.RecordEvent("timeout");
	handler.OnConnectFilteredSocketError(std::make_exception_ptr(std::runtime_error("Timeout")));
	delete this;
}

void
ConnectFilteredSocket(EventLoop &event_loop,
		      StopwatchPtr stopwatch,
		      bool ip_transparent,
		      SocketAddress bind_address,
		      SocketAddress address,
		      Event::Duration timeout,
		      SocketFilterFactory *filter_factory,
		      ConnectFilteredSocketHandler &handler,
		      CancellablePointer &cancel_ptr) noexcept
{
	auto *cfs = new ConnectFilteredSocketOperation(event_loop,
						       filter_factory,
						       std::move(stopwatch),
						       handler, cancel_ptr);
	cfs->Start(ip_transparent, bind_address, address, timeout);
}
<|endoftext|>


<commit_before>#include "compression.hpp"
#include <CCfits>
#include <cmath>
#include <valarray>
#include <vector>

using namespace CCfits;

int writeFITSImage(unsigned char *data, HeaderData keys, const std::string fileName, int width, int height)
{
    try {

    std::string timeKey;
    if (width == 0 || height == 0)
    {
        std::cerr << "Image dimension is 0. Not saving." << std::endl;
        return -1;
    }
    // declare auto-pointer to FITS at function scope. Ensures no resources
    // leaked if something fails in dynamic allocation.
    std::auto_ptr<FITS> pFits(0);

    try
    {
        pFits.reset(new FITS(fileName, BYTE_IMG, 0, 0));
    }
    catch (FITS::CantCreate)
    {
        // ... or not, as the case may be.
        return -1;
    }
    catch (FITS::CantOpen)
    {
        // ... or not, as the case may be.
        std::cerr << "Could not open file to save FITS image\n";
        return -1;
    }

    long nelements(1);

    std::vector<long int> extAx;
    extAx.push_back(width);
    extAx.push_back(height);
    string newName ("Raw Frame");

    pFits->setCompressionType(RICE_1);

    ExtHDU* imageExt;
    try{
        imageExt = pFits->addImage(newName, BYTE_IMG, extAx, 1);
    }
    catch(FitsError e){
        std::cerr << "Error while creating image extension\n";
        std::cerr << e.message() << "\n";
        return -1;
    }

    nelements = width*height*sizeof(unsigned char);

    std::valarray<unsigned char> array(data, nelements);

    long  fpixel(1);

    //add keys to the primary header
    pFits->pHDU().addKey("TELESCOP",std::string("FOXSI"),"Name of source telescope package");
//    pFits->pHDU().addKey("SIMPLE",(int)1,"always T for True, if conforming FITS file");

    pFits->pHDU().addKey("INSTRUME",std::string("SAAS"),"Name of instrument");
    pFits->pHDU().addKey("ORIGIN", std::string("FOXSI/SAAS SBC") , "Location where file was made");
    pFits->pHDU().addKey("WAVELNTH", (long)6300, "Wavelength of observation (ang)");
    pFits->pHDU().addKey("WAVE_STR", std::string("630 Nm"), "Wavelength of observation string");
    pFits->pHDU().addKey("BITPIX", (long) 8,"Bit depth of image");
    pFits->pHDU().addKey("WAVEUNIT", std::string("angstrom"), "Units of WAVELNTH");
    pFits->pHDU().addKey("PIXLUNIT", std::string("DN"), "Pixel units");
    pFits->pHDU().addKey("IMG_TYPE", std::string("LIGHT"), "Image type");
    pFits->pHDU().addKey("RSUN_REF", (double)6.9600000e+08, "");
    pFits->pHDU().addKey("CTLMODE", std::string("LIGHT"), "Image type");
    pFits->pHDU().addKey("LVL_NUM", (int) 0 , "Level of data");

    pFits->pHDU().addKey("RSUN_OBS", (double) 0, "");

    pFits->pHDU().addKey("CTYPE1", std::string("HPLN-TAN"), "A string value labeling each coordinate axis");
    pFits->pHDU().addKey("CTYPE2", std::string("HPLN-TAN"), "A string value labeling each coordinate axis");
    pFits->pHDU().addKey("CUNIT1", std::string("arcsec"), "Coordinate Units");
    pFits->pHDU().addKey("CUNIT2", std::string("arcsec"), "Coordinate Units");
    pFits->pHDU().addKey("CRVAL1", (double)0.0, "Coordinate value of the reference pixel");
    pFits->pHDU().addKey("CRVAL2", (double)0.0, "Coordinate value of the reference pixel");
    pFits->pHDU().addKey("CDELT1", (double)keys.plateScale, "Plate scale");
    pFits->pHDU().addKey("CDELT2", (double)keys.plateScale, "Plate scale");
    pFits->pHDU().addKey("CRPIX1", (double)0.0, "Reference pixel");
    pFits->pHDU().addKey("CRPIX2", (double)0.0, "Reference pixel");

    timeKey = asctime(gmtime(&(keys.captureTime).tv_sec));
    pFits->pHDU().addKey("EXPTIME", (float)keys.exposure/1e6, "Exposure time in seconds");
    pFits->pHDU().addKey("DATE_OBS", timeKey , "Date and time when observation of this image started (UTC)");
    pFits->pHDU().addKey("TEMPCCD", (float)keys.cameraTemperature, "Temperature of camera in Celsius");

    pFits->pHDU().addKey("FILENAME", fileName , "Name of the data file");
    //pFits->pHDU().addKey("TIME", 0 , "Time of observation in seconds within a day");
    pFits->pHDU().addKey("CAMERASERIAL", (int)keys.cameraID , "Serial Number of camera");
    pFits->pHDU().addKey("EXPOSURE", (int)keys.exposure,"Exposure time in usec");
    pFits->pHDU().addKey("GAIN_PRE", (float)keys.preampGain, "Preamp gain of CCD");
    pFits->pHDU().addKey("GAIN_ANA", (int)keys.analogGain, "Analog gain of CCD");
    pFits->pHDU().addKey("FRAMENUM", (long)keys.frameCount, "Frame number");

    try{
        imageExt->write(fpixel, nelements, array);
    }
    catch(FitsException e){
        std::cerr << "Exception while writing image to extension\n";
        std::cerr << e.message() << std::endl;
        return -1;
    }

    } catch (FitsError fe) {
        std::cerr << "Exception somewhere else in writeFITSImage()\n";
        std::cerr << fe.message() << std::endl;
        //throw fe;
    }

    return 0;

}<commit_msg>added camera id to FITS file<commit_after>#include "compression.hpp"
#include <CCfits>
#include <cmath>
#include <valarray>
#include <vector>

using namespace CCfits;

int writeFITSImage(unsigned char *data, HeaderData keys, const std::string fileName, int width, int height)
{
    try {

    std::string timeKey;
    if (width == 0 || height == 0)
    {
        std::cerr << "Image dimension is 0. Not saving." << std::endl;
        return -1;
    }
    // declare auto-pointer to FITS at function scope. Ensures no resources
    // leaked if something fails in dynamic allocation.
    std::auto_ptr<FITS> pFits(0);

    try
    {
        pFits.reset(new FITS(fileName, BYTE_IMG, 0, 0));
    }
    catch (FITS::CantCreate)
    {
        // ... or not, as the case may be.
        return -1;
    }
    catch (FITS::CantOpen)
    {
        // ... or not, as the case may be.
        std::cerr << "Could not open file to save FITS image\n";
        return -1;
    }

    long nelements(1);

    std::vector<long int> extAx;
    extAx.push_back(width);
    extAx.push_back(height);
    string newName ("Raw Frame");

    pFits->setCompressionType(RICE_1);

    ExtHDU* imageExt;
    try{
        imageExt = pFits->addImage(newName, BYTE_IMG, extAx, 1);
    }
    catch(FitsError e){
        std::cerr << "Error while creating image extension\n";
        std::cerr << e.message() << "\n";
        return -1;
    }

    nelements = width*height*sizeof(unsigned char);

    std::valarray<unsigned char> array(data, nelements);

    long  fpixel(1);

    //add keys to the primary header
    pFits->pHDU().addKey("TELESCOP",std::string("FOXSI"),"Name of source telescope package");
//    pFits->pHDU().addKey("SIMPLE",(int)1,"always T for True, if conforming FITS file");

    pFits->pHDU().addKey("INSTRUME",std::string("SAAS"),"Name of instrument");
    pFits->pHDU().addKey("ORIGIN", std::string("FOXSI/SAAS SBC") , "Location where file was made");
    pFits->pHDU().addKey("WAVELNTH", (long)6300, "Wavelength of observation (ang)");
    pFits->pHDU().addKey("WAVE_STR", std::string("630 Nm"), "Wavelength of observation string");
    pFits->pHDU().addKey("BITPIX", (long) 8,"Bit depth of image");
    pFits->pHDU().addKey("WAVEUNIT", std::string("angstrom"), "Units of WAVELNTH");
    pFits->pHDU().addKey("PIXLUNIT", std::string("DN"), "Pixel units");
    pFits->pHDU().addKey("IMG_TYPE", std::string("LIGHT"), "Image type");
    pFits->pHDU().addKey("RSUN_REF", (double)6.9600000e+08, "");
    pFits->pHDU().addKey("CTLMODE", std::string("LIGHT"), "Image type");
    pFits->pHDU().addKey("LVL_NUM", (int) 0 , "Level of data");

    pFits->pHDU().addKey("RSUN_OBS", (double) 0, "");

    pFits->pHDU().addKey("CTYPE1", std::string("HPLN-TAN"), "A string value labeling each coordinate axis");
    pFits->pHDU().addKey("CTYPE2", std::string("HPLN-TAN"), "A string value labeling each coordinate axis");
    pFits->pHDU().addKey("CUNIT1", std::string("arcsec"), "Coordinate Units");
    pFits->pHDU().addKey("CUNIT2", std::string("arcsec"), "Coordinate Units");
    pFits->pHDU().addKey("CRVAL1", (double)0.0, "Coordinate value of the reference pixel");
    pFits->pHDU().addKey("CRVAL2", (double)0.0, "Coordinate value of the reference pixel");
    pFits->pHDU().addKey("CDELT1", (double)keys.plateScale, "Plate scale");
    pFits->pHDU().addKey("CDELT2", (double)keys.plateScale, "Plate scale");
    pFits->pHDU().addKey("CRPIX1", (double)0.0, "Reference pixel");
    pFits->pHDU().addKey("CRPIX2", (double)0.0, "Reference pixel");

    timeKey = asctime(gmtime(&(keys.captureTime).tv_sec));
    pFits->pHDU().addKey("EXPTIME", (float)keys.exposure/1e6, "Exposure time in seconds");
    pFits->pHDU().addKey("DATE_OBS", timeKey , "Date and time when observation of this image started (UTC)");
    pFits->pHDU().addKey("TEMPCCD", (float)keys.cameraTemperature, "Temperature of camera in Celsius");

    pFits->pHDU().addKey("FILENAME", fileName , "Name of the data file");
    //pFits->pHDU().addKey("TIME", 0 , "Time of observation in seconds within a day");
    pFits->pHDU().addKey("CAMERAID", (int)keys.cameraID , "Serial Number of camera");
    pFits->pHDU().addKey("EXPOSURE", (int)keys.exposure,"Exposure time in usec");
    pFits->pHDU().addKey("GAIN_PRE", (float)keys.preampGain, "Preamp gain of CCD");
    pFits->pHDU().addKey("GAIN_ANA", (int)keys.analogGain, "Analog gain of CCD");
    pFits->pHDU().addKey("FRAMENUM", (long)keys.frameCount, "Frame number");

    try{
        imageExt->write(fpixel, nelements, array);
    }
    catch(FitsException e){
        std::cerr << "Exception while writing image to extension\n";
        std::cerr << e.message() << std::endl;
        return -1;
    }

    } catch (FitsError fe) {
        std::cerr << "Exception somewhere else in writeFITSImage()\n";
        std::cerr << fe.message() << std::endl;
        //throw fe;
    }

    return 0;

}<|endoftext|>


<commit_before>#ifndef STAN_MATH_OPENCL_REV_CHOLESKY_DECOMPOSE_HPP
#define STAN_MATH_OPENCL_REV_CHOLESKY_DECOMPOSE_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/prim/cholesky_decompose.hpp>
#include <stan/math/opencl/err/check_nan.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/value_of.hpp>
#include <stan/math/rev/functor/reverse_pass_callback.hpp>
#include <stan/math/opencl/diagonal_multiply.hpp>
#include <stan/math/opencl/triangular_transpose.hpp>

namespace stan {
namespace math {
/**
 * Returns the sum of the coefficients of the specified
 * matrix on the OpenCL device.
 *
 * @param A Specified var_value containing a matrix.
 * @return Sum of coefficients of matrix.
 */
inline var_value<matrix_cl<double>> cholesky_decompose(
    const var_value<matrix_cl<double>>& A) {
  check_nan("cholesky_decompose", "A", A.val());
  var_value<matrix_cl<double>> L_A = cholesky_decompose(A.val());

  reverse_pass_callback([A, L_A]() mutable {
    int M_ = A.rows();
    int block_size
        = M_ / opencl_context.tuning_opts().cholesky_rev_block_partition;
    block_size = std::max(block_size, 8);
    block_size = std::min(
        block_size, opencl_context.tuning_opts().cholesky_rev_min_block_size);
    matrix_cl<double> A_adj = L_A.adj();
    for (int k = M_; k > 0; k -= block_size) {
      const int j = std::max(0, k - block_size);
      const int k_j_ind = k - j;
      const int m_k_ind = M_ - k;

      auto&& R_val = block(L_A.val(), j, 0, k_j_ind, j);
      auto&& R_adj = block(A_adj, j, 0, k_j_ind, j);
      matrix_cl<double> D_val = block(L_A.val(), j, j, k_j_ind, k_j_ind);
      matrix_cl<double> D_adj = block(A_adj, j, j, k_j_ind, k_j_ind);
      auto&& B_val = block(L_A.val(), k, 0, m_k_ind, j);
      auto&& B_adj = block(A_adj, k, 0, m_k_ind, j);
      auto&& C_val = block(L_A.val(), k, j, m_k_ind, k_j_ind);
      auto&& C_adj = block(A_adj, k, j, m_k_ind, k_j_ind);

      C_adj = C_adj * tri_inverse(D_val);
      B_adj = B_adj - C_adj * R_val;
      D_adj = D_adj - transpose(C_adj) * C_val;

      D_adj = transpose(D_val) * D_adj;
      D_adj.triangular_transpose<TriangularMapCL::LowerToUpper>();
      D_val = transpose(tri_inverse(D_val));
      D_adj = D_val * transpose(D_val * D_adj);
      D_adj.triangular_transpose<TriangularMapCL::LowerToUpper>();

      R_adj = R_adj - transpose(C_adj) * B_val - D_adj * R_val;
      D_adj = diagonal_multiply(D_adj, 0.5);

      block(A_adj, j, j, k_j_ind, k_j_ind) = D_adj;
    }
    A_adj.view(matrix_cl_view::Lower);
    A.adj() = A.adj() + A_adj;
  });

  return L_A;
}

}  // namespace math
}  // namespace stan

#endif
#endif
<commit_msg>fixed doxygen<commit_after>#ifndef STAN_MATH_OPENCL_REV_CHOLESKY_DECOMPOSE_HPP
#define STAN_MATH_OPENCL_REV_CHOLESKY_DECOMPOSE_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/prim/cholesky_decompose.hpp>
#include <stan/math/opencl/err/check_nan.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/value_of.hpp>
#include <stan/math/rev/functor/reverse_pass_callback.hpp>
#include <stan/math/opencl/diagonal_multiply.hpp>
#include <stan/math/opencl/triangular_transpose.hpp>

namespace stan {
namespace math {

/**
 * Returns the lower-triangular Cholesky factor (i.e., matrix
 * square root) of the specified square, symmetric reverse mode matrix on the
 * OpenCL device. The return value \f$L\f$ will be a lower-triangular matrix
 * such that the original matrix \f$A\f$ is given by <p>\f$A = L \times L^T\f$.
 * @param A Input square matrix
 * @return Square root of matrix.
 * @throw std::domain_error if m is not a symmetric matrix or
 *   if m is not positive definite (if m has more than 0 elements)
 */
inline var_value<matrix_cl<double>> cholesky_decompose(
    const var_value<matrix_cl<double>>& A) {
  check_nan("cholesky_decompose", "A", A.val());
  var_value<matrix_cl<double>> L_A = cholesky_decompose(A.val());

  reverse_pass_callback([A, L_A]() mutable {
    int M_ = A.rows();
    int block_size
        = M_ / opencl_context.tuning_opts().cholesky_rev_block_partition;
    block_size = std::max(block_size, 8);
    block_size = std::min(
        block_size, opencl_context.tuning_opts().cholesky_rev_min_block_size);
    matrix_cl<double> A_adj = L_A.adj();
    for (int k = M_; k > 0; k -= block_size) {
      const int j = std::max(0, k - block_size);
      const int k_j_ind = k - j;
      const int m_k_ind = M_ - k;

      auto&& R_val = block(L_A.val(), j, 0, k_j_ind, j);
      auto&& R_adj = block(A_adj, j, 0, k_j_ind, j);
      matrix_cl<double> D_val = block(L_A.val(), j, j, k_j_ind, k_j_ind);
      matrix_cl<double> D_adj = block(A_adj, j, j, k_j_ind, k_j_ind);
      auto&& B_val = block(L_A.val(), k, 0, m_k_ind, j);
      auto&& B_adj = block(A_adj, k, 0, m_k_ind, j);
      auto&& C_val = block(L_A.val(), k, j, m_k_ind, k_j_ind);
      auto&& C_adj = block(A_adj, k, j, m_k_ind, k_j_ind);

      C_adj = C_adj * tri_inverse(D_val);
      B_adj = B_adj - C_adj * R_val;
      D_adj = D_adj - transpose(C_adj) * C_val;

      D_adj = transpose(D_val) * D_adj;
      D_adj.triangular_transpose<TriangularMapCL::LowerToUpper>();
      D_val = transpose(tri_inverse(D_val));
      D_adj = D_val * transpose(D_val * D_adj);
      D_adj.triangular_transpose<TriangularMapCL::LowerToUpper>();

      R_adj = R_adj - transpose(C_adj) * B_val - D_adj * R_val;
      D_adj = diagonal_multiply(D_adj, 0.5);

      block(A_adj, j, j, k_j_ind, k_j_ind) = D_adj;
    }
    A_adj.view(matrix_cl_view::Lower);
    A.adj() = A.adj() + A_adj;
  });

  return L_A;
}

}  // namespace math
}  // namespace stan

#endif
#endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_CORE_INIT_THREADPOOL_TBB_HPP
#define STAN_MATH_PRIM_CORE_INIT_THREADPOOL_TBB_HPP

#include <stan/math/prim/err/invalid_argument.hpp>

#include <boost/lexical_cast.hpp>

#ifdef TBB_INTERFACE_NEW
#include <tbb/global_control.h>
#include <tbb/task_arena.h>
#else
#include <tbb/task_scheduler_init.h>
#endif

#include <cstdlib>
#include <thread>

namespace stan {
namespace math {
namespace internal {

/**
 * Get number of threads to use. The function uses the environment
 * variable STAN_NUM_THREADS and follows these conventions:
 *
 * - STAN_NUM_THREADS is not defined => num_threads=1
 * - STAN_NUM_THREADS is positive => num_threads is set to the
 *   specified number
 * - STAN_NUM_THREADS is set to -1 => num_threads is the number of
 *   available cores on the machine
 * - STAN_NUM_THREADS < -1, STAN_NUM_THREADS = 0 or STAN_NUM_THREADS is
 *   not numeric => throws an exception
 *
 * @return number of threads to use
 * @throws std::invalid_argument if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline int get_num_threads() {
  int num_threads = 1;
  const char* env_stan_num_threads = std::getenv("STAN_NUM_THREADS");
  if (env_stan_num_threads != nullptr) {
    try {
      const int env_num_threads =
          boost::lexical_cast<int>(env_stan_num_threads);
      if (env_num_threads > 0) {
        num_threads = env_num_threads;
      } else if (env_num_threads == -1) {
        num_threads = std::thread::hardware_concurrency();
      } else {
        invalid_argument("get_num_threads(int)", "STAN_NUM_THREADS",
                         env_stan_num_threads,
                         "The STAN_NUM_THREADS environment variable is '",
                         "' but it must be positive or -1");
      }
    } catch (const boost::bad_lexical_cast&) {
      invalid_argument("get_num_threads(int)", "STAN_NUM_THREADS",
                       env_stan_num_threads,
                       "The STAN_NUM_THREADS environment variable is '",
                       "' but it must be a positive number or -1");
    }
  }
  return num_threads;
}

}  // namespace internal

#ifdef TBB_INTERFACE_NEW
/**
 * Initialize the Intel TBB threadpool and global scheduler through
 * the tbb::task_arena object. In case an instance of the
 * tbb::task_scheduler_object has been instantiated prior to calling
 * this function, then any subsequent initialization is ignored by the
 * Intel TBB.
 *
 * The maximal number of threads is read from the environment variable
 * STAN_NUM_THREADS using internal::get_num_threads. See conventions
 * of get_num_threads. The TBB scheduler will be activated by calling
 * this function.
 *
 * The function returns a reference to the static
 * tbb::global_control instance.
 *
 * @return reference to the static tbb::global_control
 * @throws std::runtime_error if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline tbb::task_arena& init_threadpool_tbb() {
  int tbb_max_threads = internal::get_num_threads();

  static tbb::global_control tbb_gc(
      tbb::global_control::max_allowed_parallelism, tbb_max_threads);

  static tbb::task_arena tbb_arena(tbb_max_threads, 1,
                                   tbb::task_arena::priority::normal);
  tbb_arena.initialize();

  return tbb_arena;
}
#else
/**
 * Initialize the Intel TBB threadpool and global scheduler through
 * the tbb::task_scheduler_init object. In case an instance of the
 * tbb::task_scheduler_object has been instantiated prior to calling
 * this function, then any subsequent initialization is ignored by the
 * Intel TBB.
 *
 * The maximal number of threads is read from the environment variable
 * STAN_NUM_THREADS using internal::get_num_threads. See conventions
 * of get_num_threads. The TBB scheduler will be activated by calling
 * this function.
 *
 * The function returns a reference to the static
 * tbb::task_scheduler_init instance.
 *
 * @param stack_size sets the stack size of each thread; the default 0
 * let's the TBB choose the stack size
 * @return reference to the static tbb::task_scheduler_init
 * @throws std::runtime_error if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline tbb::task_scheduler_init& init_threadpool_tbb(
    tbb::stack_size_type stack_size = 0) {
  int tbb_max_threads = internal::get_num_threads();

  static tbb::task_scheduler_init tbb_scheduler(tbb_max_threads, stack_size);

  return tbb_scheduler;
}
#endif

}  // namespace math
}  // namespace stan

#endif
<commit_msg>[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final)<commit_after>#ifndef STAN_MATH_PRIM_CORE_INIT_THREADPOOL_TBB_HPP
#define STAN_MATH_PRIM_CORE_INIT_THREADPOOL_TBB_HPP

#include <stan/math/prim/err/invalid_argument.hpp>

#include <boost/lexical_cast.hpp>

#ifdef TBB_INTERFACE_NEW
#include <tbb/global_control.h>
#include <tbb/task_arena.h>
#else
#include <tbb/task_scheduler_init.h>
#endif

#include <cstdlib>
#include <thread>

namespace stan {
namespace math {
namespace internal {

/**
 * Get number of threads to use. The function uses the environment
 * variable STAN_NUM_THREADS and follows these conventions:
 *
 * - STAN_NUM_THREADS is not defined => num_threads=1
 * - STAN_NUM_THREADS is positive => num_threads is set to the
 *   specified number
 * - STAN_NUM_THREADS is set to -1 => num_threads is the number of
 *   available cores on the machine
 * - STAN_NUM_THREADS < -1, STAN_NUM_THREADS = 0 or STAN_NUM_THREADS is
 *   not numeric => throws an exception
 *
 * @return number of threads to use
 * @throws std::invalid_argument if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline int get_num_threads() {
  int num_threads = 1;
  const char* env_stan_num_threads = std::getenv("STAN_NUM_THREADS");
  if (env_stan_num_threads != nullptr) {
    try {
      const int env_num_threads
          = boost::lexical_cast<int>(env_stan_num_threads);
      if (env_num_threads > 0) {
        num_threads = env_num_threads;
      } else if (env_num_threads == -1) {
        num_threads = std::thread::hardware_concurrency();
      } else {
        invalid_argument("get_num_threads(int)", "STAN_NUM_THREADS",
                         env_stan_num_threads,
                         "The STAN_NUM_THREADS environment variable is '",
                         "' but it must be positive or -1");
      }
    } catch (const boost::bad_lexical_cast&) {
      invalid_argument("get_num_threads(int)", "STAN_NUM_THREADS",
                       env_stan_num_threads,
                       "The STAN_NUM_THREADS environment variable is '",
                       "' but it must be a positive number or -1");
    }
  }
  return num_threads;
}

}  // namespace internal

#ifdef TBB_INTERFACE_NEW
/**
 * Initialize the Intel TBB threadpool and global scheduler through
 * the tbb::task_arena object. In case an instance of the
 * tbb::task_scheduler_object has been instantiated prior to calling
 * this function, then any subsequent initialization is ignored by the
 * Intel TBB.
 *
 * The maximal number of threads is read from the environment variable
 * STAN_NUM_THREADS using internal::get_num_threads. See conventions
 * of get_num_threads. The TBB scheduler will be activated by calling
 * this function.
 *
 * The function returns a reference to the static
 * tbb::global_control instance.
 *
 * @return reference to the static tbb::global_control
 * @throws std::runtime_error if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline tbb::task_arena& init_threadpool_tbb() {
  int tbb_max_threads = internal::get_num_threads();

  static tbb::global_control tbb_gc(
      tbb::global_control::max_allowed_parallelism, tbb_max_threads);

  static tbb::task_arena tbb_arena(tbb_max_threads, 1,
                                   tbb::task_arena::priority::normal);
  tbb_arena.initialize();

  return tbb_arena;
}
#else
/**
 * Initialize the Intel TBB threadpool and global scheduler through
 * the tbb::task_scheduler_init object. In case an instance of the
 * tbb::task_scheduler_object has been instantiated prior to calling
 * this function, then any subsequent initialization is ignored by the
 * Intel TBB.
 *
 * The maximal number of threads is read from the environment variable
 * STAN_NUM_THREADS using internal::get_num_threads. See conventions
 * of get_num_threads. The TBB scheduler will be activated by calling
 * this function.
 *
 * The function returns a reference to the static
 * tbb::task_scheduler_init instance.
 *
 * @param stack_size sets the stack size of each thread; the default 0
 * let's the TBB choose the stack size
 * @return reference to the static tbb::task_scheduler_init
 * @throws std::runtime_error if the value of STAN_NUM_THREADS env. variable
 * is invalid
 */
inline tbb::task_scheduler_init& init_threadpool_tbb(
    tbb::stack_size_type stack_size = 0) {
  int tbb_max_threads = internal::get_num_threads();

  static tbb::task_scheduler_init tbb_scheduler(tbb_max_threads, stack_size);

  return tbb_scheduler;
}
#endif

}  // namespace math
}  // namespace stan

#endif
<|endoftext|>