<commit_before>// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "net/disk_cache/cache_util.h"

#include <windows.h>

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

namespace {

// Deletes all the files on path that match search_name pattern.
// Do not call this function with "*" as search_name.
bool DeleteFiles(const wchar_t* path, const wchar_t* search_name) {
  std::wstring name(path);
  file_util::AppendToPath(&name, search_name);

  WIN32_FIND_DATA data;
  ScopedFindFileHandle handle(FindFirstFile(name.c_str(), &data));
  if (!handle.IsValid()) {
    DWORD error = GetLastError();
    return ERROR_FILE_NOT_FOUND == error;
  }
  std::wstring adjusted_path(path);
  adjusted_path += L'\\';
  do {
    std::wstring current(adjusted_path);
    current += data.cFileName;
    if (!DeleteFile(current.c_str()))
      return false;
  } while (FindNextFile(handle, &data));
  return true;
}

}  // namespace

namespace disk_cache {

int64 GetFreeDiskSpace(const std::wstring& path) {
  ULARGE_INTEGER available, total, free;
  if (!GetDiskFreeSpaceExW(path.c_str(), &available, &total, &free)) {
    return -1;
  }
  int64 rv = static_cast<int64>(available.QuadPart);
  if (rv < 0)
    rv = kint64max;
  return rv;
}

int64 GetSystemMemory() {
  MEMORYSTATUSEX memory_info;
  memory_info.dwLength = sizeof(memory_info);
  if (!GlobalMemoryStatusEx(&memory_info)) {
    return -1;
  }

  int64 rv = static_cast<int64>(memory_info.ullTotalPhys);
  if (rv < 0)
    rv = kint64max;
  return rv;
}

bool MoveCache(const std::wstring& from_path, const std::wstring& to_path) {
  // I don't want to use the shell version of move because if something goes
  // wrong, that version will attempt to move file by file and fail at the end.
  return MoveFileEx(from_path.c_str(), to_path.c_str(), 0) != FALSE;
}

void DeleteCache(const std::wstring& path, bool remove_folder) {
  DeleteFiles(path.c_str(), L"f_*");
  DeleteFiles(path.c_str(), L"data_*");

  std::wstring index(path);
  file_util::AppendToPath(&index, L"index");
  DeleteFile(index.c_str());

  if (remove_folder)
    RemoveDirectory(path.c_str());
}

bool DeleteCacheFile(const std::wstring& name) {
  // We do a simple delete, without ever falling back to SHFileOperation, as the
  // version from base does.
  return DeleteFile(name.c_str()) ? true : false;
}

void WaitForPendingIO(int* num_pending_io) {
  while (*num_pending_io) {
    // Asynchronous IO operations may be in flight and the completion may end
    // up calling us back so let's wait for them (we need an alertable wait).
    // The idea is to let other threads do usefull work and at the same time
    // allow more than one IO to finish... 20 mS later, we process all queued
    // APCs and see if we have to repeat the wait.
    Sleep(20);
    SleepEx(0, TRUE);
  }
}

}  // namespace disk_cache
<commit_msg>Disk cache: Delete all files from the cache folder, instead of only the files created by the cache.<commit_after>// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "net/disk_cache/cache_util.h"

#include <windows.h>

#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/file_util.h"

namespace {

// Deletes all the files on path that match search_name pattern.
void DeleteFiles(const wchar_t* path, const wchar_t* search_name) {
  std::wstring name(path);
  file_util::AppendToPath(&name, search_name);

  WIN32_FIND_DATA data;
  ScopedFindFileHandle handle(FindFirstFile(name.c_str(), &data));
  if (!handle.IsValid())
    return;

  std::wstring adjusted_path(path);
  adjusted_path += L'\\';
  do {
    if (data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY ||
        data.dwFileAttributes == FILE_ATTRIBUTE_REPARSE_POINT)
      continue;
    std::wstring current(adjusted_path);
    current += data.cFileName;
    DeleteFile(current.c_str());
  } while (FindNextFile(handle, &data));
}

}  // namespace

namespace disk_cache {

int64 GetFreeDiskSpace(const std::wstring& path) {
  ULARGE_INTEGER available, total, free;
  if (!GetDiskFreeSpaceExW(path.c_str(), &available, &total, &free)) {
    return -1;
  }
  int64 rv = static_cast<int64>(available.QuadPart);
  if (rv < 0)
    rv = kint64max;
  return rv;
}

int64 GetSystemMemory() {
  MEMORYSTATUSEX memory_info;
  memory_info.dwLength = sizeof(memory_info);
  if (!GlobalMemoryStatusEx(&memory_info)) {
    return -1;
  }

  int64 rv = static_cast<int64>(memory_info.ullTotalPhys);
  if (rv < 0)
    rv = kint64max;
  return rv;
}

bool MoveCache(const std::wstring& from_path, const std::wstring& to_path) {
  // I don't want to use the shell version of move because if something goes
  // wrong, that version will attempt to move file by file and fail at the end.
  if (!MoveFileEx(from_path.c_str(), to_path.c_str(), 0)) {
    LOG(ERROR) << "Unable to move the cache: " << GetLastError();
    return false;
  }
  return true;
}

void DeleteCache(const std::wstring& path, bool remove_folder) {
  DeleteFiles(path.c_str(), L"*");
  if (remove_folder)
    RemoveDirectory(path.c_str());
}

bool DeleteCacheFile(const std::wstring& name) {
  // We do a simple delete, without ever falling back to SHFileOperation, as the
  // version from base does.
  return DeleteFile(name.c_str()) ? true : false;
}

void WaitForPendingIO(int* num_pending_io) {
  while (*num_pending_io) {
    // Asynchronous IO operations may be in flight and the completion may end
    // up calling us back so let's wait for them (we need an alertable wait).
    // The idea is to let other threads do usefull work and at the same time
    // allow more than one IO to finish... 20 mS later, we process all queued
    // APCs and see if we have to repeat the wait.
    Sleep(20);
    SleepEx(0, TRUE);
  }
}

}  // namespace disk_cache
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2020 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 "sink_fd.hxx"
#include "Sink.hxx"
#include "UnusedPtr.hxx"
#include "pool/pool.hxx"
#include "io/Splice.hxx"
#include "io/SpliceSupport.hxx"
#include "io/FileDescriptor.hxx"
#include "event/SocketEvent.hxx"
#include "util/DestructObserver.hxx"
#include "util/LeakDetector.hxx"

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>

struct SinkFd final : IstreamSink, DestructAnchor, LeakDetector {
	struct pool *pool;

	FileDescriptor fd;
	FdType fd_type;
	const SinkFdHandler *handler;
	void *handler_ctx;

	SocketEvent event;

	/**
	 * Set to true each time data was received from the istream.
	 */
	bool got_data;

	/**
	 * This flag is used to determine if the SocketEvent::WRITE event
	 * shall be scheduled after a splice().  We need to add the event
	 * only if the splice() was triggered by SocketEvent::WRITE,
	 * because then we're responsible for querying more data.
	 */
	bool got_event = false;

#ifndef NDEBUG
	bool valid = true;
#endif

	SinkFd(EventLoop &event_loop, struct pool &_pool,
	       UnusedIstreamPtr &&_istream,
	       FileDescriptor _fd, FdType _fd_type,
	       const SinkFdHandler &_handler, void *_handler_ctx) noexcept
		:IstreamSink(std::move(_istream), istream_direct_mask_to(_fd_type)),
		 pool(&_pool),
		 fd(_fd), fd_type(_fd_type),
		 handler(&_handler), handler_ctx(_handler_ctx),
		 event(event_loop, BIND_THIS_METHOD(EventCallback),
		       SocketDescriptor::FromFileDescriptor(fd))
	{
		ScheduleWrite();
	}

	void Destroy() noexcept {
		this->~SinkFd();
	}

	bool IsDefined() const noexcept {
		return input.IsDefined();
	}

	void Read() noexcept {
		input.Read();
	}

	void Close() noexcept {
		input.Close();
		Destroy();
	}

	void ScheduleWrite() noexcept {
		assert(fd.IsDefined());
		assert(input.IsDefined());

		got_event = false;
		event.ScheduleWrite();
	}

	void EventCallback(unsigned events) noexcept;

	/* virtual methods from class IstreamHandler */
	size_t OnData(const void *data, size_t length) noexcept override;
	ssize_t OnDirect(FdType type, int fd, size_t max_length) noexcept override;
	void OnEof() noexcept override;
	void OnError(std::exception_ptr ep) noexcept override;
};

/*
 * istream handler
 *
 */

size_t
SinkFd::OnData(const void *data, size_t length) noexcept
{
	got_data = true;

	ssize_t nbytes = IsAnySocket(fd_type)
		? send(fd.Get(), data, length, MSG_DONTWAIT|MSG_NOSIGNAL)
		: fd.Write(data, length);
	if (nbytes >= 0) {
		ScheduleWrite();
		return nbytes;
	} else if (errno == EAGAIN) {
		ScheduleWrite();
		return 0;
	} else {
		event.Cancel();
		if (handler->send_error(errno, handler_ctx)) {
			input.Close();
			Destroy();
		}
		return 0;
	}
}

ssize_t
SinkFd::OnDirect(FdType type, int _fd, size_t max_length) noexcept
{
	got_data = true;

	ssize_t nbytes = SpliceTo(_fd, type, fd.Get(), fd_type, max_length);
	if (gcc_unlikely(nbytes < 0 && errno == EAGAIN)) {
		if (!fd.IsReadyForWriting()) {
			ScheduleWrite();
			return ISTREAM_RESULT_BLOCKING;
		}

		/* try again, just in case connection->fd has become ready
		   between the first istream_direct_to_socket() call and
		   fd_ready_for_writing() */
		nbytes = SpliceTo(_fd, type, fd.Get(), fd_type, max_length);
	}

	if (gcc_likely(nbytes > 0) && (got_event || type == FdType::FD_FILE))
		/* regular files don't have support for SocketEvent::READ, and
		   thus the sink is responsible for triggering the next
		   splice */
		ScheduleWrite();

	return nbytes;
}

void
SinkFd::OnEof() noexcept
{
	got_data = true;

#ifndef NDEBUG
	valid = false;
#endif

	event.Cancel();

	handler->input_eof(handler_ctx);
	Destroy();
}

void
SinkFd::OnError(std::exception_ptr ep) noexcept
{
	got_data = true;

#ifndef NDEBUG
	valid = false;
#endif

	event.Cancel();

	handler->input_error(ep, handler_ctx);
	Destroy();
}

/*
 * libevent callback
 *
 */

inline void
SinkFd::EventCallback(unsigned) noexcept
{
	const DestructObserver destructed(*this);

	got_event = true;
	got_data = false;
	input.Read();

	if (!destructed && !got_data)
		/* the fd is ready for writing, but the istream is blocking -
		   don't try again for now */
		event.Cancel();
}

/*
 * constructor
 *
 */

SinkFd *
sink_fd_new(EventLoop &event_loop, struct pool &pool, UnusedIstreamPtr istream,
	    FileDescriptor fd, FdType fd_type,
	    const SinkFdHandler &handler, void *ctx) noexcept
{
	assert(fd.IsDefined());
	assert(handler.input_eof != nullptr);
	assert(handler.input_error != nullptr);
	assert(handler.send_error != nullptr);

	return NewFromPool<SinkFd>(pool, event_loop, pool, std::move(istream),
				   fd, fd_type,
				   handler, ctx);
}

void
sink_fd_read(SinkFd *ss) noexcept
{
	assert(ss != nullptr);
	assert(ss->valid);
	assert(ss->IsDefined());

	ss->Read();
}

void
sink_fd_close(SinkFd *ss) noexcept
{
	assert(ss != nullptr);
	assert(ss->valid);
	assert(ss->IsDefined());

#ifndef NDEBUG
	ss->valid = false;
#endif

	ss->event.Cancel();
	ss->Close();
}
<commit_msg>istream/sink_fd: convert SinkFd to class<commit_after>/*
 * Copyright 2007-2020 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 "sink_fd.hxx"
#include "Sink.hxx"
#include "UnusedPtr.hxx"
#include "pool/pool.hxx"
#include "io/Splice.hxx"
#include "io/SpliceSupport.hxx"
#include "io/FileDescriptor.hxx"
#include "event/SocketEvent.hxx"
#include "util/DestructObserver.hxx"
#include "util/LeakDetector.hxx"

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>

class SinkFd final : IstreamSink, DestructAnchor, LeakDetector {
	struct pool *pool;

	FileDescriptor fd;
	FdType fd_type;
	const SinkFdHandler *handler;
	void *handler_ctx;

	SocketEvent event;

	/**
	 * Set to true each time data was received from the istream.
	 */
	bool got_data;

	/**
	 * This flag is used to determine if the SocketEvent::WRITE event
	 * shall be scheduled after a splice().  We need to add the event
	 * only if the splice() was triggered by SocketEvent::WRITE,
	 * because then we're responsible for querying more data.
	 */
	bool got_event = false;

#ifndef NDEBUG
	bool valid = true;
#endif

public:
	SinkFd(EventLoop &event_loop, struct pool &_pool,
	       UnusedIstreamPtr &&_istream,
	       FileDescriptor _fd, FdType _fd_type,
	       const SinkFdHandler &_handler, void *_handler_ctx) noexcept
		:IstreamSink(std::move(_istream), istream_direct_mask_to(_fd_type)),
		 pool(&_pool),
		 fd(_fd), fd_type(_fd_type),
		 handler(&_handler), handler_ctx(_handler_ctx),
		 event(event_loop, BIND_THIS_METHOD(EventCallback),
		       SocketDescriptor::FromFileDescriptor(fd))
	{
		ScheduleWrite();
	}

	void Destroy() noexcept {
		this->~SinkFd();
	}

	bool IsDefined() const noexcept {
		return input.IsDefined();
	}

	void Read() noexcept {
		assert(valid);
		assert(IsDefined());

		input.Read();
	}

	void Close() noexcept {
#ifndef NDEBUG
		valid = false;
#endif

		event.Cancel();
		input.Close();
		Destroy();
	}

private:
	void ScheduleWrite() noexcept {
		assert(fd.IsDefined());
		assert(input.IsDefined());

		got_event = false;
		event.ScheduleWrite();
	}

	void EventCallback(unsigned events) noexcept;

	/* virtual methods from class IstreamHandler */
	size_t OnData(const void *data, size_t length) noexcept override;
	ssize_t OnDirect(FdType type, int fd, size_t max_length) noexcept override;
	void OnEof() noexcept override;
	void OnError(std::exception_ptr ep) noexcept override;
};

/*
 * istream handler
 *
 */

size_t
SinkFd::OnData(const void *data, size_t length) noexcept
{
	got_data = true;

	ssize_t nbytes = IsAnySocket(fd_type)
		? send(fd.Get(), data, length, MSG_DONTWAIT|MSG_NOSIGNAL)
		: fd.Write(data, length);
	if (nbytes >= 0) {
		ScheduleWrite();
		return nbytes;
	} else if (errno == EAGAIN) {
		ScheduleWrite();
		return 0;
	} else {
		event.Cancel();
		if (handler->send_error(errno, handler_ctx)) {
			input.Close();
			Destroy();
		}
		return 0;
	}
}

ssize_t
SinkFd::OnDirect(FdType type, int _fd, size_t max_length) noexcept
{
	got_data = true;

	ssize_t nbytes = SpliceTo(_fd, type, fd.Get(), fd_type, max_length);
	if (gcc_unlikely(nbytes < 0 && errno == EAGAIN)) {
		if (!fd.IsReadyForWriting()) {
			ScheduleWrite();
			return ISTREAM_RESULT_BLOCKING;
		}

		/* try again, just in case connection->fd has become ready
		   between the first istream_direct_to_socket() call and
		   fd_ready_for_writing() */
		nbytes = SpliceTo(_fd, type, fd.Get(), fd_type, max_length);
	}

	if (gcc_likely(nbytes > 0) && (got_event || type == FdType::FD_FILE))
		/* regular files don't have support for SocketEvent::READ, and
		   thus the sink is responsible for triggering the next
		   splice */
		ScheduleWrite();

	return nbytes;
}

void
SinkFd::OnEof() noexcept
{
	got_data = true;

#ifndef NDEBUG
	valid = false;
#endif

	event.Cancel();

	handler->input_eof(handler_ctx);
	Destroy();
}

void
SinkFd::OnError(std::exception_ptr ep) noexcept
{
	got_data = true;

#ifndef NDEBUG
	valid = false;
#endif

	event.Cancel();

	handler->input_error(ep, handler_ctx);
	Destroy();
}

/*
 * libevent callback
 *
 */

inline void
SinkFd::EventCallback(unsigned) noexcept
{
	const DestructObserver destructed(*this);

	got_event = true;
	got_data = false;
	input.Read();

	if (!destructed && !got_data)
		/* the fd is ready for writing, but the istream is blocking -
		   don't try again for now */
		event.Cancel();
}

/*
 * constructor
 *
 */

SinkFd *
sink_fd_new(EventLoop &event_loop, struct pool &pool, UnusedIstreamPtr istream,
	    FileDescriptor fd, FdType fd_type,
	    const SinkFdHandler &handler, void *ctx) noexcept
{
	assert(fd.IsDefined());
	assert(handler.input_eof != nullptr);
	assert(handler.input_error != nullptr);
	assert(handler.send_error != nullptr);

	return NewFromPool<SinkFd>(pool, event_loop, pool, std::move(istream),
				   fd, fd_type,
				   handler, ctx);
}

void
sink_fd_read(SinkFd *ss) noexcept
{
	assert(ss != nullptr);

	ss->Read();
}

void
sink_fd_close(SinkFd *ss) noexcept
{
	assert(ss != nullptr);

	ss->Close();
}
<|endoftext|>


<commit_before>/*
Napisz program, który kolejno:
- utworzy 10-elementową tablicę liczb typu int
- uzupełni tablicę losowymi wartościami
- wypisze elementy tablicy na ekran
- wypisze na ekran średnią arytmetyczną, geometryczną oraz
medianę elementów tablicy
*/
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

#define SIZE 10

using std::endl;
using std::cout;

int main() {

    int tab[SIZE];
    srand(time(NULL));

    dousuma{0};
    double iloczyn{1};
    float mediana{0};

    for (int i = 0; i < SIZE; i++) {
        tab[i] = rand();

        suma += tab[i];
        iloczyn *= tab[i];

        cout << tab[i] << " ";
    }

    int i{SIZE / 2};
    if (SIZE % 2 == 0) {
        mediana = (tab[i] + tab[i + 1]) / 2;
    } else {
        mediana = tab[i] / 2;
    }

    cout << endl;
    cout << "średnia arytmetyczna = " << suma / SIZE << endl;
    cout << "średnia geometryczna = " << pow(iloczyn, 1.0 / SIZE) << endl;
    cout << "mediana = " << mediana;

    cout << endl;
}
<commit_msg>fix, ograniczenie do 100<commit_after>/*
Napisz program, który kolejno:
- utworzy 10-elementową tablicę liczb typu int
- uzupełni tablicę losowymi wartościami
- wypisze elementy tablicy na ekran
- wypisze na ekran średnią arytmetyczną, geometryczną oraz
medianę elementów tablicy
*/
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

#define SIZE 10

using std::endl;
using std::cout;

int main() {

    int tab[SIZE];
    srand(time(NULL));

    double suma{0};
    double iloczyn{1};
    float mediana{0};

    for (int i = 0; i < SIZE; i++) {
        tab[i] = rand() % 100; // ograniczenie do 100

        suma += tab[i];
        iloczyn *= tab[i];

        cout << tab[i] << " ";
    }

    int i{SIZE / 2};
    if (SIZE % 2 == 0) {
        mediana = (tab[i] + tab[i + 1]) / 2;
    } else {
        mediana = tab[i] / 2;
    }

    cout << endl;
    cout << "średnia arytmetyczna = " << suma / SIZE << endl;
    cout << "średnia geometryczna = " << pow(iloczyn, 1.0 / SIZE) << endl;
    cout << "mediana = " << mediana;

    cout << endl;
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions 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 materialsprovided 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
// COPYRIGHT HOLDER 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 <algorithm>
#include <functional>
#include <memory>
#include <deque>
#include <set>
#include <vector>
#include <boost/asio.hpp>
#include "chat_message.h"

using boost::asio::ip::tcp;

typedef std::deque<ChatMessage> ChatMessageQueue;

class ChatParticipant {
public:
  virtual ~ChatParticipant(void) {}
  virtual void deliver(const ChatMessage& msg) = 0;
};

typedef std::shared_ptr<ChatParticipant> ChatParticipantPtr;

class ChatRoom : private boost::noncopyable {
  enum { MAX_RECENT_NMSG = 100 };
  std::set<ChatParticipantPtr> participants_;
  ChatMessageQueue recent_msgs_;
public:
  void join(const ChatParticipantPtr& participant) {
    participants_.insert(participant);
    std::for_each(recent_msgs_.begin(), recent_msgs_.end(),
        [this](const ChatMessage& msg) {
          deliver(msg);
        });
  }

  void leave(const ChatParticipantPtr& participant) {
    participants_.erase(participant);
  }

  void deliver(const ChatMessage& msg) {
    recent_msgs_.push_back(msg);
    while (recent_msgs_.size() > MAX_RECENT_NMSG)
      recent_msgs_.pop_front();

    std::for_each(participants_.begin(), participants_.end(),
        std::bind(&ChatParticipant::deliver, std::placeholders::_1, std::ref(msg)));
  }
};

class ChatSession : public ChatParticipant, public std::enable_shared_from_this<ChatSession> {
  tcp::socket socket_;
  ChatRoom& room_;
  ChatMessage rmsg_;
  ChatMessageQueue wmsg_queue_;

  void do_start(void) {
    auto self(shared_from_this());
    boost::asio::async_read(socket_, boost::asio::buffer(rmsg_.data(), rmsg_.get_nheader()),
        [this, self](const boost::system::error_code& ec, std::size_t /*read_bytes*/) {
          if (!ec && rmsg_.decode_header()) {
            boost::asio::async_read(socket_, boost::asio::buffer(rmsg_.body(), rmsg_.get_nbody()),
              [this, self](const boost::system::error_code& ec, std::size_t /*read_bytes*/) {
                if (!ec)
                  do_start();
                else
                  room_.leave(shared_from_this());
              });
          }
          else {
            room_.leave(shared_from_this());
          }
        });
  }

  void do_write(const boost::system::error_code& ec) {
    if (!ec) {
      wmsg_queue_.pop_front();
      if (!wmsg_queue_.empty()) {
        auto self(shared_from_this());
        boost::asio::async_write(socket_,
            boost::asio::buffer(wmsg_queue_.front().data(), wmsg_queue_.front().size()),
            [this, self](const boost::system::error_code& ec, std::size_t /*n*/) {
              do_write(ec);
            });
      }
    }
    else {
      room_.leave(shared_from_this());
    }
  }
public:
  ChatSession(tcp::socket&& socket, ChatRoom& room)
    : socket_(std::move(socket))
    , room_(room) {
  }

  tcp::socket& get_socket(void) {
    return socket_;
  }

  void start(void) {
    room_.join(shared_from_this());
    do_start();
  }

  void deliver(const ChatMessage& msg) {
    bool write_in_progress = !wmsg_queue_.empty();
    wmsg_queue_.push_back(msg);
    if (!write_in_progress) {
      auto self(shared_from_this());
      boost::asio::async_write(socket_, boost::asio::buffer(wmsg_queue_.front().data(), wmsg_queue_.front().size()),
          [this, self](const boost::system::error_code& ec, std::size_t /*written_bytes*/) {
            do_write(ec);
          });
    }
  }
};

class ChatServer : private boost::noncopyable {
  tcp::acceptor acceptor_;
  tcp::socket socket_;

  void do_accept(void) {
    acceptor_.async_accept(socket_,
        [this](const boost::system::error_code& ec) {
          if (!ec)
            std::make_shared<ChatSession>(std::move(socket_))->start();

          do_accept();
        });
  }
public:
  ChatServer(boost::asio::io_service& io_service, const tcp::endpoint& endpoint)
    : acceptor_(io_service, endpoint)
    , socket_(io_service) {
  }

  void start(void) {
    do_accept();
  }
};

int main(int argc, char* argv[]) {
  (void)argc, (void)argv;

  boost::asio::io_service io_service;

  ChatServer server(io_service, tcp::endpoint(tcp::v4(), 5555));
  server.start();

  io_service.run();

  return 0;
}
<commit_msg>:construction: chore(chat.server): updated the chat server implementation<commit_after>// Copyright (c) 2017 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions 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 materialsprovided 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
// COPYRIGHT HOLDER 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 <algorithm>
#include <functional>
#include <memory>
#include <deque>
#include <set>
#include <vector>
#include <boost/asio.hpp>
#include "chat_message.h"

using boost::asio::ip::tcp;

typedef std::deque<ChatMessage> ChatMessageQueue;

class ChatParticipant {
public:
  virtual ~ChatParticipant(void) {}
  virtual void deliver(const ChatMessage& msg) = 0;
};

typedef std::shared_ptr<ChatParticipant> ChatParticipantPtr;

class ChatRoom : private boost::noncopyable {
  enum { MAX_RECENT_NMSG = 100 };
  std::set<ChatParticipantPtr> participants_;
  ChatMessageQueue recent_msgs_;
public:
  void join(const ChatParticipantPtr& participant) {
    participants_.insert(participant);
    std::for_each(recent_msgs_.begin(), recent_msgs_.end(),
        [this](const ChatMessage& msg) {
          deliver(msg);
        });
  }

  void leave(const ChatParticipantPtr& participant) {
    participants_.erase(participant);
  }

  void deliver(const ChatMessage& msg) {
    recent_msgs_.push_back(msg);
    while (recent_msgs_.size() > MAX_RECENT_NMSG)
      recent_msgs_.pop_front();

    std::for_each(participants_.begin(), participants_.end(),
        std::bind(&ChatParticipant::deliver, std::placeholders::_1, std::ref(msg)));
  }
};

class ChatSession : public ChatParticipant, public std::enable_shared_from_this<ChatSession> {
  tcp::socket socket_;
  ChatRoom& room_;
  ChatMessage rmsg_;
  ChatMessageQueue wmsg_queue_;

  void do_start(void) {
    auto self(shared_from_this());
    boost::asio::async_read(socket_, boost::asio::buffer(rmsg_.data(), rmsg_.get_nheader()),
        [this, self](const boost::system::error_code& ec, std::size_t /*read_bytes*/) {
          if (!ec && rmsg_.decode_header()) {
            boost::asio::async_read(socket_, boost::asio::buffer(rmsg_.body(), rmsg_.get_nbody()),
              [this, self](const boost::system::error_code& ec, std::size_t /*read_bytes*/) {
                if (!ec)
                  do_start();
                else
                  room_.leave(shared_from_this());
              });
          }
          else {
            room_.leave(shared_from_this());
          }
        });
  }

  void do_write(const boost::system::error_code& ec) {
    if (!ec) {
      wmsg_queue_.pop_front();
      if (!wmsg_queue_.empty()) {
        auto self(shared_from_this());
        boost::asio::async_write(socket_,
            boost::asio::buffer(wmsg_queue_.front().data(), wmsg_queue_.front().size()),
            [this, self](const boost::system::error_code& ec, std::size_t /*n*/) {
              do_write(ec);
            });
      }
    }
    else {
      room_.leave(shared_from_this());
    }
  }
public:
  ChatSession(tcp::socket&& socket, ChatRoom& room)
    : socket_(std::move(socket))
    , room_(room) {
  }

  tcp::socket& get_socket(void) {
    return socket_;
  }

  void start(void) {
    room_.join(shared_from_this());
    do_start();
  }

  void deliver(const ChatMessage& msg) {
    bool write_in_progress = !wmsg_queue_.empty();
    wmsg_queue_.push_back(msg);
    if (!write_in_progress) {
      auto self(shared_from_this());
      boost::asio::async_write(socket_, boost::asio::buffer(wmsg_queue_.front().data(), wmsg_queue_.front().size()),
          [this, self](const boost::system::error_code& ec, std::size_t /*written_bytes*/) {
            do_write(ec);
          });
    }
  }
};

class ChatServer : private boost::noncopyable {
  tcp::acceptor acceptor_;
  tcp::socket socket_;
  ChatRoom room_;

  void do_accept(void) {
    acceptor_.async_accept(socket_,
        [this](const boost::system::error_code& ec) {
          if (!ec)
            std::make_shared<ChatSession>(std::move(socket_), room_)->start();

          do_accept();
        });
  }
public:
  ChatServer(boost::asio::io_service& io_service, const tcp::endpoint& endpoint)
    : acceptor_(io_service, endpoint)
    , socket_(io_service) {
  }

  void start(void) {
    do_accept();
  }
};

int main(int argc, char* argv[]) {
  (void)argc, (void)argv;

  boost::asio::io_service io_service;

  ChatServer server(io_service, tcp::endpoint(tcp::v4(), 5555));
  server.start();

  io_service.run();

  return 0;
}
<|endoftext|>


<commit_before>/*
Generic simulation cell class.

Copyright 2013, 2014, 2015 Ilja Honkonen
All rights reserved.

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

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

* Neither the name of copyright holders nor the names of their contributors
  may be used to endorse or promote products derived from this software
  without specific prior written permission.

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

#ifndef GENSIMCELL_HPP
#define GENSIMCELL_HPP

#include "tuple"

#include "assign.hpp"
#include "operators.hpp"
#include "type_support.hpp"
#include "gensimcell_impl.hpp"
#include "gensimcell_transfer_policy.hpp"

/*!
\mainpage Generic simulation cell class.

See gensimcell::Cell to get started with the API.
*/

/*!
Namespace where everything related to
generic simulation cell class is defined.
*/
namespace gensimcell {

/*!
Generic simulation cell that stores arbitrary simulation variables.

Transfer policy decides how generic cells of that type will transfer
variable data between processes. gensimcell::Optional_Transfer allows
to switch on and off transfer of variable data in individual cells.
gensimcell::Always_Transfer makes cells of that type always transfer
all of their variables' data. gensimcell::Never_Transfer never
transfers any variables' data between processes. The latter two
policies can be used to create cells with smaller memory footprint,
cells using gensimcell::Optional_Transfer as a transfer policy
store in memory one boolean before or after each variables' data.
See below for details on switching transfers on and off.

Simulation variables are classes given as template arguments.
Each variables must define its type as data_type which will
be the type stored in each instance of the cell class.
For example in Conway's Game of Life (GoL) the following variables
could be used:
@code
struct Is_Alive { using data_type = bool;};
struct Live_Neighbors { using data_type = int;};
@endcode
An instance of a GoL cell is created by:
@code
gensimcell::Cell<Is_Alive, Live_Neighbors> cell;
@endcode
And a grid of such cells can be created by:
@code
using Cell_T = gensimcell::Cell<Is_Alive, Live_Neighbors>;
std::array<std::array<Cell_T, 10>, 10> grid;
@endcode
Access to the data of each variable is provided by the []
operator overloaded for the class type:
@code
cell[Is_Alive()] = false;
cell[Live_Neighbors()] = 3;
@endcode
The life state of all cells in the previous grid can be printed by:
@code
for (const auto& row: grid) {
	for (const auto& cell: row) {
		cout << cell[Is_Alive()] << " ";
	}
	cout << endl;
}
@endcode
For complete examples see the following files in the git repository:
examples/game_of_life/serial.cpp
examples/advection/serial.cpp
examples/particle_propagation/serial.cpp

Distributed memory concurrency support is provided using the
Message Passing Interface (MPI) application programming interface
via the get_mpi_datatype() member function. Each variable stored
in the cell can be included in the transfer information returned
by get_mpi_datatype(). Whether a variable is included or not can
be switched with the set_transfer() and set_transfer_all()
methods. The latter switches the transfer of variables given to
the method on or off in all instances of that cell, the former
affects only the current cell. When the value set by
set_transfer_all() is equal to true of false, all cells
will behave identically regardless of the value set by
set_transfer(). To get individual cell behavior for a variable
set the transfer info using set_transfer_all() to an
undeterminate value for the specific variables.
For complete examples see the files in the following directories
in the git repository:
examples/game_of_life/parallel/
examples/advection/parallel/
examples/particle_propagation/parallel/
examples/combined/

For the full API see the comments in the
source/gensimcell_impl.hpp file.
*/
template <
	template<class> class Transfer_Policy,
	class... Variables
> class Cell :
	public detail::Cell_impl<Transfer_Policy, sizeof...(Variables), Variables...>
{
public:
	/*!
	Allows the cell class to be stored as a variable in another cell class.

	For example:
	@code
	struct GoL_Variables1 {
		using data_type = gensimcell:Cell<Is_Alive, Live_Neighbors>;
	};
	struct GoL_Variables2 {
		using data_type = gensimcell:Cell<Is_Alive, Live_Neighbors>;
	};
	gensimcell::Cell<GoL_Variables1, GoL_Variables2> cell1, cell2;

	cell1[GoL_Variables1()][Is_Alive()] =
	cell1[GoL_Variables2()][Is_Alive()] =
	cell2[GoL_Variables1()][Is_Alive()] =
	cell2[GoL_Variables2()][Is_Alive()] = true;
	@endcode
	*/
	using data_type = detail::Cell_impl<
		Transfer_Policy,
		sizeof...(Variables),
		Variables...
	>;

	/*!
	*/
	template<class Other> void assign(const Other& other)
	{
		gensimcell::assign(*this, other);
	}

	#if defined(MPI_VERSION) && (MPI_VERSION >= 2)

	/*!
	Returns the MPI transfer info of this cell's variables.

	Each variable that has been set to be transferred with
	set_transfer_all() or set_transfer() will be added to the
	final datatype returned by this function. See the MPI API
	for details on the returned address, count and MPI_Datatype.
	*/
	std::tuple<
		void*,
		int,
		MPI_Datatype
	> get_mpi_datatype() const
	{
		return detail::Cell_impl<
			Transfer_Policy,
			sizeof...(Variables),
			Variables...
		>::get_mpi_datatype();
	}

	#endif // ifdef MPI_VERSION
};

namespace detail {

//! get_last::type is equal to last given template parameter
template<class... T> struct get_last;

template<class T, class... U> struct get_last<T, U...> {
	using type = typename get_last<U...>::type;
};

template<class T> struct get_last<T> {
	using type = T;
};

} // namespace detail

/*!
Returns a (const) reference to data stored recursively in Variables.

Helper function for accessing variables nested in several layers
of generic simulation cells.

Calls [] operator of given cell with first variable, calls []
of type returned by first [] with second variable, etc. The call
get(c, v1, v2, v3) is equal to c[v1][v2][v3].

Example:
@code
struct var1 {using data_type = int};
struct var2 {using data_type = gensimcell::Cell<..., var1>};
gensimcell::Cell<..., var2> cell;
gensimcell::get(cell, var2(), var1()) = 3;
@endcode
*/
template<class Cell, class... Variables> void get(
	const Cell&,
	const Variables&...
) {}

//! Starts/continues recursion over variables
template<
	class Cell_T,
	class First_Variable,
	class... Rest_Of_Variables
> const typename detail::get_last<
	First_Variable,
	Rest_Of_Variables...
>::type::data_type& get(
	const Cell_T& cell,
	const First_Variable& first_variable,
	const Rest_Of_Variables&... rest_of_variables
) {
	return get(cell[first_variable], rest_of_variables...);
}

//! Non-const version of recursion over variables
template<
	class Cell_T,
	class First_Variable,
	class... Rest_Of_Variables
> typename detail::get_last<
	First_Variable,
	Rest_Of_Variables...
>::type::data_type& get(
	Cell_T& cell,
	const First_Variable& first_variable,
	const Rest_Of_Variables&... rest_of_variables
) {
	return get(cell[first_variable], rest_of_variables...);
}

//! Stops recursion over variables
template<
	class Cell_T,
	class Last_Variable
> const typename Last_Variable::data_type& get(
	const Cell_T& cell,
	const Last_Variable& variable
) {
	return cell[variable];
}

//! Non-const version of stopping recursion
template<
	class Cell_T,
	class Last_Variable
> typename Last_Variable::data_type& get(
	Cell_T& cell,
	const Last_Variable& variable
) {
	return cell[variable];
}

} // namespace gensimcell

#endif // ifndef GENSIMCELL_HPP
<commit_msg>Update example in documentation of cell class to include transfer policy.<commit_after>/*
Generic simulation cell class.

Copyright 2013, 2014, 2015 Ilja Honkonen
All rights reserved.

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

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

* Neither the name of copyright holders nor the names of their contributors
  may be used to endorse or promote products derived from this software
  without specific prior written permission.

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

#ifndef GENSIMCELL_HPP
#define GENSIMCELL_HPP

#include "tuple"

#include "assign.hpp"
#include "operators.hpp"
#include "type_support.hpp"
#include "gensimcell_impl.hpp"
#include "gensimcell_transfer_policy.hpp"

/*!
\mainpage Generic simulation cell class.

See gensimcell::Cell to get started with the API.
*/

/*!
Namespace where everything related to
generic simulation cell class is defined.
*/
namespace gensimcell {

/*!
Generic simulation cell that stores arbitrary simulation variables.

Transfer policy decides how generic cells of that type will transfer
variable data between processes. gensimcell::Optional_Transfer allows
to switch on and off transfer of variable data in individual cells.
gensimcell::Always_Transfer makes cells of that type always transfer
all of their variables' data. gensimcell::Never_Transfer never
transfers any variables' data between processes. The latter two
policies can be used to create cells with smaller memory footprint,
cells using gensimcell::Optional_Transfer as a transfer policy
store in memory one boolean before or after each variables' data.
See below for details on switching transfers on and off.

Simulation variables are classes given as template arguments.
Each variables must define its type as data_type which will
be the type stored in each instance of the cell class.
For example in Conway's Game of Life (GoL) the following variables
could be used:
@code
struct Is_Alive { using data_type = bool;};
struct Live_Neighbors { using data_type = int;};
@endcode
An instance of a GoL cell is created by:
@code
gensimcell::Cell<gensimcell::Always_Transfer, Is_Alive, Live_Neighbors> cell;
@endcode
And a grid of such cells can be created by:
@code
using Cell = gensimcell::Cell<gensimcell::Always_Transfer, Is_Alive, Live_Neighbors>;
std::array<std::array<Cell, 10>, 10> grid;
@endcode
Access to the data of each variable is provided by the []
operator overloaded for the class type:
@code
cell[Is_Alive()] = false;
cell[Live_Neighbors()] = 3;
@endcode
The life state of all cells in the previous grid can be printed by:
@code
for (const auto& row: grid) {
	for (const auto& cell: row) {
		cout << cell[Is_Alive()] << " ";
	}
	cout << endl;
}
@endcode
For complete examples see the following files in the git repository:
examples/game_of_life/serial.cpp
examples/advection/serial.cpp
examples/particle_propagation/serial.cpp

Distributed memory concurrency support is provided using the
Message Passing Interface (MPI) application programming interface
via the get_mpi_datatype() member function. Each variable stored
in the cell can be included in the transfer information returned
by get_mpi_datatype(). Whether a variable is included or not can
be switched with the set_transfer() and set_transfer_all()
methods. The latter switches the transfer of variables given to
the method on or off in all instances of that cell, the former
affects only the current cell. When the value set by
set_transfer_all() is equal to true of false, all cells
will behave identically regardless of the value set by
set_transfer(). To get individual cell behavior for a variable
set the transfer info using set_transfer_all() to an
undeterminate value for the specific variables.
For complete examples see the files in the following directories
in the git repository:
examples/game_of_life/parallel/
examples/advection/parallel/
examples/particle_propagation/parallel/
examples/combined/

For the full API see the comments in the
source/gensimcell_impl.hpp file.
*/
template <
	template<class> class Transfer_Policy,
	class... Variables
> class Cell :
	public detail::Cell_impl<Transfer_Policy, sizeof...(Variables), Variables...>
{
public:
	/*!
	Allows the cell class to be stored as a variable in another cell class.

	For example:
	@code
	struct GoL_Variables1 {
		using data_type = gensimcell:Cell<Is_Alive, Live_Neighbors>;
	};
	struct GoL_Variables2 {
		using data_type = gensimcell:Cell<Is_Alive, Live_Neighbors>;
	};
	gensimcell::Cell<GoL_Variables1, GoL_Variables2> cell1, cell2;

	cell1[GoL_Variables1()][Is_Alive()] =
	cell1[GoL_Variables2()][Is_Alive()] =
	cell2[GoL_Variables1()][Is_Alive()] =
	cell2[GoL_Variables2()][Is_Alive()] = true;
	@endcode
	*/
	using data_type = detail::Cell_impl<
		Transfer_Policy,
		sizeof...(Variables),
		Variables...
	>;

	/*!
	*/
	template<class Other> void assign(const Other& other)
	{
		gensimcell::assign(*this, other);
	}

	#if defined(MPI_VERSION) && (MPI_VERSION >= 2)

	/*!
	Returns the MPI transfer info of this cell's variables.

	Each variable that has been set to be transferred with
	set_transfer_all() or set_transfer() will be added to the
	final datatype returned by this function. See the MPI API
	for details on the returned address, count and MPI_Datatype.
	*/
	std::tuple<
		void*,
		int,
		MPI_Datatype
	> get_mpi_datatype() const
	{
		return detail::Cell_impl<
			Transfer_Policy,
			sizeof...(Variables),
			Variables...
		>::get_mpi_datatype();
	}

	#endif // ifdef MPI_VERSION
};

namespace detail {

//! get_last::type is equal to last given template parameter
template<class... T> struct get_last;

template<class T, class... U> struct get_last<T, U...> {
	using type = typename get_last<U...>::type;
};

template<class T> struct get_last<T> {
	using type = T;
};

} // namespace detail

/*!
Returns a (const) reference to data stored recursively in Variables.

Helper function for accessing variables nested in several layers
of generic simulation cells.

Calls [] operator of given cell with first variable, calls []
of type returned by first [] with second variable, etc. The call
get(c, v1, v2, v3) is equal to c[v1][v2][v3].

Example:
@code
struct var1 {using data_type = int};
struct var2 {using data_type = gensimcell::Cell<..., var1>};
gensimcell::Cell<..., var2> cell;
gensimcell::get(cell, var2(), var1()) = 3;
@endcode
*/
template<class Cell, class... Variables> void get(
	const Cell&,
	const Variables&...
) {}

//! Starts/continues recursion over variables
template<
	class Cell_T,
	class First_Variable,
	class... Rest_Of_Variables
> const typename detail::get_last<
	First_Variable,
	Rest_Of_Variables...
>::type::data_type& get(
	const Cell_T& cell,
	const First_Variable& first_variable,
	const Rest_Of_Variables&... rest_of_variables
) {
	return get(cell[first_variable], rest_of_variables...);
}

//! Non-const version of recursion over variables
template<
	class Cell_T,
	class First_Variable,
	class... Rest_Of_Variables
> typename detail::get_last<
	First_Variable,
	Rest_Of_Variables...
>::type::data_type& get(
	Cell_T& cell,
	const First_Variable& first_variable,
	const Rest_Of_Variables&... rest_of_variables
) {
	return get(cell[first_variable], rest_of_variables...);
}

//! Stops recursion over variables
template<
	class Cell_T,
	class Last_Variable
> const typename Last_Variable::data_type& get(
	const Cell_T& cell,
	const Last_Variable& variable
) {
	return cell[variable];
}

//! Non-const version of stopping recursion
template<
	class Cell_T,
	class Last_Variable
> typename Last_Variable::data_type& get(
	Cell_T& cell,
	const Last_Variable& variable
) {
	return cell[variable];
}

} // namespace gensimcell

#endif // ifndef GENSIMCELL_HPP
<|endoftext|>


<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <cstdlib>
#include <mutex>

#include "caf/async/consumer.hpp"
#include "caf/async/producer.hpp"
#include "caf/config.hpp"
#include "caf/defaults.hpp"
#include "caf/error.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/make_counted.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::async {

/// Policy type for having `consume` call `on_error` immediately after the
/// producer has aborted even if the buffer still contains events.
struct prioritize_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates prioritize_errors_t
constexpr auto prioritize_errors = prioritize_errors_t{};

/// Policy type for having `consume` call `on_error` only after processing all
/// events from the buffer.
struct delay_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates delay_errors_t
constexpr auto delay_errors = delay_errors_t{};

/// A bounded buffer for transmitting events from one producer to one consumer.
template <class T>
class bounded_buffer : public ref_counted {
public:
  using value_type = T;

  bounded_buffer(uint32_t max_in_flight, uint32_t min_pull_size)
    : max_in_flight_(max_in_flight), min_pull_size_(min_pull_size) {
    buf_.reserve(max_in_flight);
    consumer_buf_.reserve(max_in_flight);
  }

  /// Appends to the buffer and calls `on_producer_wakeup` on the consumer if
  /// the buffer becomes non-empty.
  /// @returns the remaining capacity after inserting the items.
  size_t push(span<const T> items) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(producer_ != nullptr);
    CAF_ASSERT(!closed_);
    CAF_ASSERT(buf_.size() + items.size() <= max_in_flight_);
    buf_.insert(buf_.end(), items.begin(), items.end());
    if (buf_.size() == items.size() && consumer_)
      consumer_->on_producer_wakeup();
    return capacity() - buf_.size();
  }

  size_t push(const T& item) {
    return push(make_span(&item, 1));
  }

  /// Consumes up to `demand` items from the buffer.
  /// @tparam Policy Either `instant_error_t`, `delay_error_t` or
  ///                `ignore_errors_t`. The former two policies require also
  ///                passing an `on_error` handler.
  /// @returns true if the consumer may call `pull` again, otherwise `false`.
  ///          When returning `false`, the function has called `on_complete` or
  ///          `on_error` on the observer.
  template <class Policy, class Observer>
  std::pair<bool, size_t> pull(Policy, size_t demand, Observer& dst) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(consumer_ != nullptr);
    CAF_ASSERT(consumer_buf_.empty());
    if constexpr (std::is_same_v<Policy, prioritize_errors_t>) {
      if (err_) {
        consumer_ = nullptr;
        dst.on_error(err_);
        return {false, 0};
      }
    }
    auto next_n = [this, &demand] { return std::min(demand, buf_.size()); };
    size_t consumed = 0;
    for (auto n = next_n(); n > 0; n = next_n()) {
      using std::make_move_iterator;
      consumer_buf_.assign(make_move_iterator(buf_.begin()),
                           make_move_iterator(buf_.begin() + n));
      buf_.erase(buf_.begin(), buf_.begin() + n);
      signal_demand(n);
      guard.unlock();
      dst.on_next(span<const T>{consumer_buf_.data(), n});
      demand -= n;
      consumed += n;
      consumer_buf_.clear();
      guard.lock();
    }
    if (!buf_.empty() || !closed_) {
      return {true, consumed};
    } else {
      consumer_ = nullptr;
      if (err_)
        dst.on_error(err_);
      else
        dst.on_complete();
      return {false, consumed};
    }
  }

  /// Checks whether there is any pending data in the buffer.
  bool has_data() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty();
  }

  /// Checks whether the there is data available or the producer has closed or
  /// aborted the flow.
  bool has_consumer_event() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty() || closed_;
  }

  /// Returns how many items are currently available.
  size_t available() const noexcept {
    std::unique_lock guard{mtx_};
    return buf_.size();
  }

  /// Closes the buffer by request of the producer.
  void close() {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer and signals an error by request of the producer.
  void abort(error reason) {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      err_ = std::move(reason);
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer by request of the consumer.
  void cancel() {
    std::unique_lock guard{mtx_};
    if (consumer_) {
      consumer_ = nullptr;
      if (producer_)
        producer_->on_consumer_cancel();
    }
  }

  void set_consumer(consumer_ptr consumer) {
    CAF_ASSERT(consumer != nullptr);
    std::unique_lock guard{mtx_};
    if (consumer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a consumer");
    consumer_ = std::move(consumer);
    if (producer_)
      ready();
  }

  void set_producer(producer_ptr producer) {
    CAF_ASSERT(producer != nullptr);
    std::unique_lock guard{mtx_};
    if (producer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a producer");
    producer_ = std::move(producer);
    if (consumer_)
      ready();
  }

  size_t capacity() const noexcept {
    return max_in_flight_;
  }

private:
  void ready() {
    producer_->on_consumer_ready();
    consumer_->on_producer_ready();
    if (!buf_.empty())
      consumer_->on_producer_wakeup();
    signal_demand(max_in_flight_);
  }

  void signal_demand(uint32_t new_demand) {
    demand_ += new_demand;
    if (demand_ >= min_pull_size_ && producer_) {
      producer_->on_consumer_demand(demand_);
      demand_ = 0;
    }
  }

  /// Guards access to all other member variables.
  mutable std::mutex mtx_;

  /// Allocated to max_in_flight_ * 2, but at most holds max_in_flight_
  /// elements at any point in time. We dynamically shift elements into the
  /// first half of the buffer whenever rd_pos_ crosses the midpoint.
  std::vector<T> buf_;

  /// Stores how many items the buffer may hold at any time.
  uint32_t max_in_flight_;

  /// Configures the minimum amount of free buffer slots that we signal to the
  /// producer.
  uint32_t min_pull_size_;

  /// Demand that has not yet been signaled back to the producer.
  uint32_t demand_ = 0;

  /// Stores whether `close` has been called.
  bool closed_ = false;

  /// Stores the abort reason.
  error err_;

  /// Callback handle to the consumer.
  consumer_ptr consumer_;

  /// Callback handle to the producer.
  producer_ptr producer_;

  /// Caches elements before passing them to the consumer (without lock).
  std::vector<T> consumer_buf_;
};

/// @relates bounded_buffer
template <class T>
using bounded_buffer_ptr = intrusive_ptr<bounded_buffer<T>>;

/// @relates bounded_buffer
template <class T, bool IsProducer>
struct resource_ctrl : ref_counted {
  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit resource_ctrl(buffer_ptr ptr) : buf(std::move(ptr)) {
    // nop
  }

  ~resource_ctrl() {
    if (buf) {
      if constexpr (IsProducer) {
        auto err = make_error(sec::invalid_upstream,
                              "producer_resource destroyed without opening it");
        buf->abort(err);
      } else {
        buf->cancel();
      }
    }
  }

  buffer_ptr try_open() {
    std::unique_lock guard{mtx};
    if (buf) {
      auto res = buffer_ptr{};
      res.swap(buf);
      return res;
    }
    return nullptr;
  }

  mutable std::mutex mtx;
  buffer_ptr buf;
};

/// Grants read access to the first consumer that calls `open` on the resource.
/// Cancels consumption of items on the buffer if the resources gets destroyed
/// before opening it.
/// @relates bounded_buffer
template <class T>
class consumer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit consumer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

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

  /// Tries to open the resource for reading from the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, false>> ctrl_;
};

/// Grants access to a buffer to the first producer that calls `open`. Aborts
/// writes on the buffer if the resources gets destroyed before opening it.
/// @relates bounded_buffer
template <class T>
class producer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit producer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

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

  /// Tries to open the resource for writing to the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, true>> ctrl_;
};

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource(size_t buffer_size, size_t min_request_size) {
  using buffer_type = bounded_buffer<T>;
  auto buf = make_counted<buffer_type>(buffer_size, min_request_size);
  return {async::consumer_resource<T>{buf}, async::producer_resource<T>{buf}};
}

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource() {
  return make_bounded_buffer_resource<T>(defaults::flow::buffer_size,
                                         defaults::flow::min_demand);
}

} // namespace caf::async
<commit_msg>Extend API to allow locking by client code<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <cstdlib>
#include <mutex>

#include "caf/async/consumer.hpp"
#include "caf/async/producer.hpp"
#include "caf/config.hpp"
#include "caf/defaults.hpp"
#include "caf/error.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/make_counted.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::async {

/// Policy type for having `consume` call `on_error` immediately after the
/// producer has aborted even if the buffer still contains events.
struct prioritize_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates prioritize_errors_t
constexpr auto prioritize_errors = prioritize_errors_t{};

/// Policy type for having `consume` call `on_error` only after processing all
/// events from the buffer.
struct delay_errors_t {
  static constexpr bool calls_on_error = true;
};

/// @relates delay_errors_t
constexpr auto delay_errors = delay_errors_t{};

/// A bounded buffer for transmitting events from one producer to one consumer.
template <class T>
class bounded_buffer : public ref_counted {
public:
  using value_type = T;

  bounded_buffer(uint32_t max_in_flight, uint32_t min_pull_size)
    : max_in_flight_(max_in_flight), min_pull_size_(min_pull_size) {
    buf_.reserve(max_in_flight);
    consumer_buf_.reserve(max_in_flight);
  }

  /// Appends to the buffer and calls `on_producer_wakeup` on the consumer if
  /// the buffer becomes non-empty.
  /// @returns the remaining capacity after inserting the items.
  size_t push(span<const T> items) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(producer_ != nullptr);
    CAF_ASSERT(!closed_);
    CAF_ASSERT(buf_.size() + items.size() <= max_in_flight_);
    buf_.insert(buf_.end(), items.begin(), items.end());
    if (buf_.size() == items.size() && consumer_)
      consumer_->on_producer_wakeup();
    return capacity() - buf_.size();
  }

  size_t push(const T& item) {
    return push(make_span(&item, 1));
  }

  /// Consumes up to `demand` items from the buffer.
  /// @tparam Policy Either `instant_error_t`, `delay_error_t` or
  ///                `ignore_errors_t`. The former two policies require also
  ///                passing an `on_error` handler.
  /// @returns true if the consumer may call `pull` again, otherwise `false`.
  ///          When returning `false`, the function has called `on_complete` or
  ///          `on_error` on the observer.
  template <class Policy, class Observer>
  std::pair<bool, size_t> pull(Policy, size_t demand, Observer& dst) {
    std::unique_lock guard{mtx_};
    CAF_ASSERT(consumer_ != nullptr);
    CAF_ASSERT(consumer_buf_.empty());
    if constexpr (std::is_same_v<Policy, prioritize_errors_t>) {
      if (err_) {
        consumer_ = nullptr;
        dst.on_error(err_);
        return {false, 0};
      }
    }
    auto next_n = [this, &demand] { return std::min(demand, buf_.size()); };
    size_t consumed = 0;
    for (auto n = next_n(); n > 0; n = next_n()) {
      using std::make_move_iterator;
      consumer_buf_.assign(make_move_iterator(buf_.begin()),
                           make_move_iterator(buf_.begin() + n));
      buf_.erase(buf_.begin(), buf_.begin() + n);
      signal_demand(n);
      guard.unlock();
      dst.on_next(span<const T>{consumer_buf_.data(), n});
      demand -= n;
      consumed += n;
      consumer_buf_.clear();
      guard.lock();
    }
    if (!buf_.empty() || !closed_) {
      return {true, consumed};
    } else {
      consumer_ = nullptr;
      if (err_)
        dst.on_error(err_);
      else
        dst.on_complete();
      return {false, consumed};
    }
  }

  /// Checks whether there is any pending data in the buffer.
  bool has_data() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty();
  }

  /// Checks whether the there is data available or the producer has closed or
  /// aborted the flow.
  bool has_consumer_event() const noexcept {
    std::unique_lock guard{mtx_};
    return !buf_.empty() || closed_;
  }

  /// Returns how many items are currently available.
  size_t available() const noexcept {
    std::unique_lock guard{mtx_};
    return buf_.size();
  }

  /// Closes the buffer by request of the producer.
  void close() {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer and signals an error by request of the producer.
  void abort(error reason) {
    std::unique_lock guard{mtx_};
    if (producer_) {
      closed_ = true;
      err_ = std::move(reason);
      producer_ = nullptr;
      if (buf_.empty() && consumer_)
        consumer_->on_producer_wakeup();
    }
  }

  /// Closes the buffer by request of the consumer.
  void cancel() {
    std::unique_lock guard{mtx_};
    if (consumer_) {
      consumer_ = nullptr;
      if (producer_)
        producer_->on_consumer_cancel();
    }
  }

  void set_consumer(consumer_ptr consumer) {
    CAF_ASSERT(consumer != nullptr);
    std::unique_lock guard{mtx_};
    if (consumer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a consumer");
    consumer_ = std::move(consumer);
    if (producer_)
      ready();
  }

  void set_producer(producer_ptr producer) {
    CAF_ASSERT(producer != nullptr);
    std::unique_lock guard{mtx_};
    if (producer_)
      CAF_RAISE_ERROR("producer-consumer queue already has a producer");
    producer_ = std::move(producer);
    if (consumer_)
      ready();
  }

  size_t capacity() const noexcept {
    return max_in_flight_;
  }

  /// Returns the mutex for this object.
  auto& mtx() const noexcept {
    return mtx_;
  }

  /// Returns how many items are currently available.
  /// @pre 'mtx()' is locked.
  size_t available_unsafe() const noexcept {
    return buf_.size();
  }

private:
  void ready() {
    producer_->on_consumer_ready();
    consumer_->on_producer_ready();
    if (!buf_.empty())
      consumer_->on_producer_wakeup();
    signal_demand(max_in_flight_);
  }

  void signal_demand(uint32_t new_demand) {
    demand_ += new_demand;
    if (demand_ >= min_pull_size_ && producer_) {
      producer_->on_consumer_demand(demand_);
      demand_ = 0;
    }
  }

  /// Guards access to all other member variables.
  mutable std::mutex mtx_;

  /// Allocated to max_in_flight_ * 2, but at most holds max_in_flight_
  /// elements at any point in time. We dynamically shift elements into the
  /// first half of the buffer whenever rd_pos_ crosses the midpoint.
  std::vector<T> buf_;

  /// Stores how many items the buffer may hold at any time.
  uint32_t max_in_flight_;

  /// Configures the minimum amount of free buffer slots that we signal to the
  /// producer.
  uint32_t min_pull_size_;

  /// Demand that has not yet been signaled back to the producer.
  uint32_t demand_ = 0;

  /// Stores whether `close` has been called.
  bool closed_ = false;

  /// Stores the abort reason.
  error err_;

  /// Callback handle to the consumer.
  consumer_ptr consumer_;

  /// Callback handle to the producer.
  producer_ptr producer_;

  /// Caches elements before passing them to the consumer (without lock).
  std::vector<T> consumer_buf_;
};

/// @relates bounded_buffer
template <class T>
using bounded_buffer_ptr = intrusive_ptr<bounded_buffer<T>>;

/// @relates bounded_buffer
template <class T, bool IsProducer>
struct resource_ctrl : ref_counted {
  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit resource_ctrl(buffer_ptr ptr) : buf(std::move(ptr)) {
    // nop
  }

  ~resource_ctrl() {
    if (buf) {
      if constexpr (IsProducer) {
        auto err = make_error(sec::invalid_upstream,
                              "producer_resource destroyed without opening it");
        buf->abort(err);
      } else {
        buf->cancel();
      }
    }
  }

  buffer_ptr try_open() {
    std::unique_lock guard{mtx};
    if (buf) {
      auto res = buffer_ptr{};
      res.swap(buf);
      return res;
    }
    return nullptr;
  }

  mutable std::mutex mtx;
  buffer_ptr buf;
};

/// Grants read access to the first consumer that calls `open` on the resource.
/// Cancels consumption of items on the buffer if the resources gets destroyed
/// before opening it.
/// @relates bounded_buffer
template <class T>
class consumer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit consumer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

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

  /// Tries to open the resource for reading from the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, false>> ctrl_;
};

/// Grants access to a buffer to the first producer that calls `open`. Aborts
/// writes on the buffer if the resources gets destroyed before opening it.
/// @relates bounded_buffer
template <class T>
class producer_resource {
public:
  using value_type = T;

  using buffer_type = bounded_buffer<T>;

  using buffer_ptr = bounded_buffer_ptr<T>;

  explicit producer_resource(buffer_ptr buf) {
    ctrl_.emplace(std::move(buf));
  }

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

  /// Tries to open the resource for writing to the buffer. The first `open`
  /// wins on concurrent access.
  /// @returns a pointer to the buffer on success, `nullptr` otherwise.
  buffer_ptr try_open() {
    if (ctrl_) {
      auto res = ctrl_->try_open();
      ctrl_ = nullptr;
      return res;
    } else {
      return nullptr;
    }
  }

private:
  intrusive_ptr<resource_ctrl<T, true>> ctrl_;
};

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource(size_t buffer_size, size_t min_request_size) {
  using buffer_type = bounded_buffer<T>;
  auto buf = make_counted<buffer_type>(buffer_size, min_request_size);
  return {async::consumer_resource<T>{buf}, async::producer_resource<T>{buf}};
}

/// Creates bounded buffer and returns two resources connected by that buffer.
template <class T>
std::pair<consumer_resource<T>, producer_resource<T>>
make_bounded_buffer_resource() {
  return make_bounded_buffer_resource<T>(defaults::flow::buffer_size,
                                         defaults::flow::min_demand);
}

} // namespace caf::async
<|endoftext|>


<commit_before>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <iostream>
#include "qiremoteobject_p.h"
#include "qmetaobjectbuilder_p.h"
#include "qimetatype_p.h"

#include <qimessaging/datastream.hpp>
#include <qimessaging/object.hpp>
#include <QtCore/qdebug.h>
#include <qimessaging/signature.hpp>
#include <qimessaging/transport_socket.hpp>

class QiRemoteObjectPrivate {
public:
  qi::TransportSocket *socket;
  std::string          dest;
  QMetaObject         *meta;
  unsigned int         serviceId;
};

QiRemoteObject::QiRemoteObject(qi::TransportSocket *ts, const std::string &dest, unsigned int serviceId, const qi::MetaObject &metaobject)
  : _p (new QiRemoteObjectPrivate)
{
  QMetaObjectBuilder mob;
  mob.setClassName(dest.c_str());

  std::vector<qi::MetaMethod>::const_iterator it;
  for (it = metaobject._methods.begin(); it != metaobject._methods.end(); ++it) {
    const qi::MetaMethod &mm = *it;
    QString retSig;
    QString funSig;
    qi_SignatureToMetaMethod(mm.signature(), &retSig, &funSig);
    //qDebug() << "Ret: " << retSig;
    //qDebug() << "Fun: " << funSig;
    QMetaMethodBuilder mmb = mob.addMethod(funSig.toUtf8(), retSig.toUtf8());
    mmb.setTag(mm.signature().c_str());
  }

  _p->socket = ts;
  _p->dest = dest;
  _p->serviceId = serviceId;
  _p->meta = mob.toMetaObject();
}

QiRemoteObject::~QiRemoteObject()
{
}

const QMetaObject* QiRemoteObject::metaObject() const {
  return _p->meta;
};

int                QiRemoteObject::qt_metacall(QMetaObject::Call c, int id, void **a) {
  qi::Message    msg;
  qi::Message    retmsg;
  qi::DataStream args(msg.buffer());

  if (c != QMetaObject::InvokeMetaMethod)
    return id;

  QMetaMethod method             = _p->meta->method(id);
  const int returnType           = QMetaType::type(method.typeName());
  const QList<QByteArray> pTypes = method.parameterTypes();
  const int pTypesCount          = pTypes.count();

  for (int i = 0; i < pTypesCount; i++) {
    const QByteArray &t = pTypes[i];
    const int ttype     = QMetaType::type(t);
    qi_MetaTypeStore(args, ttype, a[i+1]);
  }

  QString methodname(method.signature());
  methodname.truncate(methodname.indexOf('('));

  msg.setType(qi::Message::Type_Call);
  msg.setService(_p->serviceId);
  msg.setFunction(id - _p->meta->methodOffset());
  _p->socket->send(msg);

  _p->socket->waitForId(msg.id());
  _p->socket->read(msg.id(), &retmsg);

  qi::DataStream retv(retmsg.buffer());

  qi_MetaTypeLoad(retv, returnType, a[0]);

  return 0;
};

void              *QiRemoteObject::qt_metacast(const char* className) {
  std::cout << "qt_metacast:" << className << std::endl;
  return 0;
}

<commit_msg>Add missing setPath in QiRemoteObject::qt_metacall().<commit_after>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <iostream>
#include "qiremoteobject_p.h"
#include "qmetaobjectbuilder_p.h"
#include "qimetatype_p.h"

#include <qimessaging/datastream.hpp>
#include <qimessaging/object.hpp>
#include <QtCore/qdebug.h>
#include <qimessaging/signature.hpp>
#include <qimessaging/transport_socket.hpp>

class QiRemoteObjectPrivate {
public:
  qi::TransportSocket *socket;
  std::string          dest;
  QMetaObject         *meta;
  unsigned int         serviceId;
};

QiRemoteObject::QiRemoteObject(qi::TransportSocket *ts, const std::string &dest, unsigned int serviceId, const qi::MetaObject &metaobject)
  : _p (new QiRemoteObjectPrivate)
{
  QMetaObjectBuilder mob;
  mob.setClassName(dest.c_str());

  std::vector<qi::MetaMethod>::const_iterator it;
  for (it = metaobject._methods.begin(); it != metaobject._methods.end(); ++it) {
    const qi::MetaMethod &mm = *it;
    QString retSig;
    QString funSig;
    qi_SignatureToMetaMethod(mm.signature(), &retSig, &funSig);
    //qDebug() << "Ret: " << retSig;
    //qDebug() << "Fun: " << funSig;
    QMetaMethodBuilder mmb = mob.addMethod(funSig.toUtf8(), retSig.toUtf8());
    mmb.setTag(mm.signature().c_str());
  }

  _p->socket = ts;
  _p->dest = dest;
  _p->serviceId = serviceId;
  _p->meta = mob.toMetaObject();
}

QiRemoteObject::~QiRemoteObject()
{
}

const QMetaObject* QiRemoteObject::metaObject() const {
  return _p->meta;
};

int                QiRemoteObject::qt_metacall(QMetaObject::Call c, int id, void **a) {
  qi::Message    msg;
  qi::Message    retmsg;
  qi::DataStream args(msg.buffer());

  if (c != QMetaObject::InvokeMetaMethod)
    return id;

  QMetaMethod method             = _p->meta->method(id);
  const int returnType           = QMetaType::type(method.typeName());
  const QList<QByteArray> pTypes = method.parameterTypes();
  const int pTypesCount          = pTypes.count();

  for (int i = 0; i < pTypesCount; i++) {
    const QByteArray &t = pTypes[i];
    const int ttype     = QMetaType::type(t);
    qi_MetaTypeStore(args, ttype, a[i+1]);
  }

  QString methodname(method.signature());
  methodname.truncate(methodname.indexOf('('));

  msg.setType(qi::Message::Type_Call);
  msg.setService(_p->serviceId);
  msg.setPath(qi::Message::Path_Main);
  msg.setFunction(id - _p->meta->methodOffset());
  _p->socket->send(msg);

  _p->socket->waitForId(msg.id());
  _p->socket->read(msg.id(), &retmsg);

  qi::DataStream retv(retmsg.buffer());

  qi_MetaTypeLoad(retv, returnType, a[0]);

  return 0;
};

void              *QiRemoteObject::qt_metacast(const char* className) {
  std::cout << "qt_metacast:" << className << std::endl;
  return 0;
}

<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
//
//  File:  $Source: /usr/sci/projects/Nektar/cvs/Nektar++/libs/SpatialDomains/EdgeComponent.cpp,v $
//
//  For more information, please see: http://www.nektar.info/
//
//  The MIT License
//
//  Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
//  Department of Aeronautics, Imperial College London (UK), and Scientific
//  Computing and Imaging Institute, University of Utah (USA).
//
//  License for the specific language governing rights and limitations under
//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included
//  in all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
//  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.
//
//  Description:
//
//
////////////////////////////////////////////////////////////////////////////////
#include "pchSpatialDomains.h"

#include <SpatialDomains/EdgeComponent.h>

namespace Nektar
{
    namespace SpatialDomains
    {
        EdgeComponent::EdgeComponent()
        {
        }

        EdgeComponent::EdgeComponent(int id, const int coordim):
        Geometry1D(coordim)
        {

            const StdRegions::BasisKey B(StdRegions::eModified_A, 2,
                StdRegions::eLobatto, 3,0,0);
            m_eid = id;

            m_xmap = new StdRegions::StdSegExp * [m_coordim];

            for(int i = 0; i < m_coordim; ++i)
            {
                m_xmap[i] =  new StdRegions::StdSegExp(B);
            }
        }

        EdgeComponent::EdgeComponent(int id, const int coordim,
            VertexComponentSharedPtr vertex[]):
        Geometry1D(coordim),
            m_vertex(2) //always have two vertices per edge
        {
            m_eid = id;

            if (coordim > 0)
            {
                const StdRegions::BasisKey B(StdRegions::eModified_A, 2,
                    StdRegions::eLobatto, 3,0,0);

                m_xmap = new StdRegions::StdSegExp * [m_coordim];

                for(int i = 0; i < m_coordim; ++i)
                {
                    m_xmap[i] = new StdRegions::StdSegExp(B);
                }
            }

            m_vertex[0] = vertex[0];
            m_vertex[1] = vertex[1];
        }

        EdgeComponent::EdgeComponent(int id, const int coordim,
            const int order, const int nquad):
        Geometry1D(coordim)
        {

            const StdRegions::BasisKey B(StdRegions::eModified_A, order,
                StdRegions::eLobatto, nquad,0,0);

            m_xmap = new StdRegions::StdSegExp * [m_coordim];

            for(int i = 0; i < m_coordim; ++i)
            {
                m_xmap[i] = new StdRegions::StdSegExp(B);
            }

            m_state = eNotFilled;
        }

        EdgeComponent::~EdgeComponent()
        {
            if(m_xmap[0])
            {
                for(int i =0; i < m_coordim; ++i)
                {
                    delete m_xmap[i];
                }

                delete[] m_xmap;

                m_xmap[0] = (StdRegions::StdSegExp *) NULL;
            }
        }

        EdgeComponent::EdgeComponent(const EdgeComponent &T)
        {

            m_eid = T.m_eid;

            std::list<CompToElmt>::const_iterator def;
            for(def = T.m_elmtmap.begin(); def != T.m_elmtmap.end(); def++)
            {
                m_elmtmap.push_back(*def);
            }
        }

        /** given local collapsed coordinate Lcoord return the value of
        physical coordinate in direction i **/

        double EdgeComponent::GetCoord(const int i, const double *Lcoord)
        {

            ASSERTL1(m_state == ePtsFilled, "Goemetry is not in physical space");

            return m_xmap[i]->Evaluate(Lcoord);
        }

        void EdgeComponent::AddElmtConnected(int gvo_id, int locid)
        {
            CompToElmt ee(gvo_id,locid);
            m_elmtmap.push_back(ee);
        }

        int EdgeComponent::NumElmtConnected() const
        {
            return int(m_elmtmap.size());
        }

        bool EdgeComponent::IsElmtConnected(int gvo_id, int locid) const
        {
            std::list<CompToElmt>::const_iterator def;
            CompToElmt ee(gvo_id,locid);

            def = find(m_elmtmap.begin(),m_elmtmap.end(),ee);

            // Found the element connectivity object in the list
            if(def != m_elmtmap.end())
            {
                return(true);
            }

            return(false);
        }

        /// \brief Get the orientation of edge1.
        ///
        /// If edge1 is connected to edge2 in the same direction as
        /// the points comprising edge1 then it is forward, otherwise
        /// it is backward.
        ///
        /// For example, assume edge1 is comprised of points 1 and 2,
        /// and edge2 is comprised of points 2 and 3, then edge is
        /// forward.
        ///
        /// If edge1 is comprised of points 2 and 1 and edge2 is
        /// comprised of points 3 and 2, then edge1 is backward.

        StdRegions::EdgeOrientation EdgeComponent::GetEdgeOrientation(const EdgeComponent &edge1,  const EdgeComponent &edge2)
        {
            StdRegions::EdgeOrientation returnval = StdRegions::eForwards;

            /// Backward direction.  Vertex 0 is connected to edge 2.
            if ((*edge1.GetVertex(0) == *edge2.GetVertex(0)) ||
                (*edge1.GetVertex(0) == *edge2.GetVertex(1)))
            {
                returnval = StdRegions::eBackwards;
            }
            // Not forward either, then we have a problem.
            else if ((*edge1.GetVertex(1) != *edge2.GetVertex(0)) &&
                (*edge1.GetVertex(1) != *edge2.GetVertex(1)))
            {
                std::ostrstream errstrm;
                errstrm << "Connected edges do not share a vertex. Edges ";
                errstrm << edge1.GetEid() << ", " << edge2.GetEid();
                ASSERTL0(false, errstrm.str());
            }

            return returnval;
        }
    }; //end of namespace
}; //end of namespace

/**
*    $Log: EdgeComponent.cpp,v $
*    Revision 1.9  2006/07/02 17:16:17  sherwin
*
*    Modifications to make MultiRegions work for a connected domain in 2D (Tris)
*
*    Revision 1.8  2006/06/02 18:48:40  sherwin
*    Modifications to make ProjectLoc2D run bit there are bus errors for order > 3
*
*    Revision 1.7  2006/05/30 14:00:04  sherwin
*    Updates to make MultiRegions and its Demos work
*
*    Revision 1.6  2006/05/23 20:19:58  jfrazier
*    Added #pragma to show where problems currently exist.
*
*    Revision 1.5  2006/05/23 19:56:33  jfrazier
*    These build and run, but the expansion pieces are commented out
*    because they would not run.
*
*    Revision 1.4  2006/05/16 20:12:59  jfrazier
*    Minor fixes to correct bugs.
*
*    Revision 1.3  2006/05/09 13:37:01  jfrazier
*    Removed duplicate definition of shared vertex pointer.
*
*    Revision 1.2  2006/05/06 20:36:16  sherwin
*    Modifications to get LocalRegions/Project1D working
*
*    Revision 1.1  2006/05/04 18:58:59  kirby
*    *** empty log message ***
*
*    Revision 1.14  2006/04/11 23:18:11  jfrazier
*    Completed MeshGraph2D for tri's and quads.  Not thoroughly tested.
*
*    Revision 1.13  2006/04/09 02:08:34  jfrazier
*    Added precompiled header.
*
*    Revision 1.12  2006/04/04 23:12:37  jfrazier
*    More updates to readers.  Still work to do on MeshGraph2D to store tris and quads.
*
*    Revision 1.11  2006/03/25 00:58:28  jfrazier
*    Many changes dealing with fundamental structure and reading/writing.
*
*    Revision 1.10  2006/03/12 14:20:42  sherwin
*
*    First compiling version of SpatialDomains and associated modifications
*
*    Revision 1.9  2006/03/12 11:06:39  sherwin
*
*    First complete copy of code standard code but still not compile tested
*
*    Revision 1.8  2006/03/12 07:42:02  sherwin
*
*    Updated member names and StdRegions call. Still has not been compiled
*
**/
<commit_msg>Eliminated bug where an array was indexed after it was deleted.<commit_after>////////////////////////////////////////////////////////////////////////////////
//
//  File:  $Source: /usr/sci/projects/Nektar/cvs/Nektar++/libs/SpatialDomains/EdgeComponent.cpp,v $
//
//  For more information, please see: http://www.nektar.info/
//
//  The MIT License
//
//  Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
//  Department of Aeronautics, Imperial College London (UK), and Scientific
//  Computing and Imaging Institute, University of Utah (USA).
//
//  License for the specific language governing rights and limitations under
//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included
//  in all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
//  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.
//
//  Description:
//
//
////////////////////////////////////////////////////////////////////////////////
#include "pchSpatialDomains.h"

#include <SpatialDomains/EdgeComponent.h>

namespace Nektar
{
    namespace SpatialDomains
    {
        EdgeComponent::EdgeComponent()
        {
        }

        EdgeComponent::EdgeComponent(int id, const int coordim):
        Geometry1D(coordim)
        {

            const StdRegions::BasisKey B(StdRegions::eModified_A, 2,
                StdRegions::eLobatto, 3,0,0);
            m_eid = id;

            m_xmap = new StdRegions::StdSegExp * [m_coordim];

            for(int i = 0; i < m_coordim; ++i)
            {
                m_xmap[i] =  new StdRegions::StdSegExp(B);
            }
        }

        EdgeComponent::EdgeComponent(int id, const int coordim,
            VertexComponentSharedPtr vertex[]):
        Geometry1D(coordim),
            m_vertex(2) //always have two vertices per edge
        {
            m_eid = id;

            if (coordim > 0)
            {
                const StdRegions::BasisKey B(StdRegions::eModified_A, 2,
                    StdRegions::eLobatto, 3,0,0);

                m_xmap = new StdRegions::StdSegExp * [m_coordim];

                for(int i = 0; i < m_coordim; ++i)
                {
                    m_xmap[i] = new StdRegions::StdSegExp(B);
                }
            }

            m_vertex[0] = vertex[0];
            m_vertex[1] = vertex[1];
        }

        EdgeComponent::EdgeComponent(int id, const int coordim,
            const int order, const int nquad):
        Geometry1D(coordim)
        {

            const StdRegions::BasisKey B(StdRegions::eModified_A, order,
                StdRegions::eLobatto, nquad,0,0);

            m_xmap = new StdRegions::StdSegExp * [m_coordim];

            for(int i = 0; i < m_coordim; ++i)
            {
                m_xmap[i] = new StdRegions::StdSegExp(B);
            }

            m_state = eNotFilled;
        }

        EdgeComponent::~EdgeComponent()
        {
            if(m_xmap)
            {
                for(int i =0; i < m_coordim; ++i)
                {
                    delete m_xmap[i];
                }

                delete[] m_xmap;
                m_xmap = NULL;
            }
        }

        EdgeComponent::EdgeComponent(const EdgeComponent &T)
        {

            m_eid = T.m_eid;

            std::list<CompToElmt>::const_iterator def;
            for(def = T.m_elmtmap.begin(); def != T.m_elmtmap.end(); def++)
            {
                m_elmtmap.push_back(*def);
            }
        }

        /** given local collapsed coordinate Lcoord return the value of
        physical coordinate in direction i **/

        double EdgeComponent::GetCoord(const int i, const double *Lcoord)
        {

            ASSERTL1(m_state == ePtsFilled, "Goemetry is not in physical space");

            return m_xmap[i]->Evaluate(Lcoord);
        }

        void EdgeComponent::AddElmtConnected(int gvo_id, int locid)
        {
            CompToElmt ee(gvo_id,locid);
            m_elmtmap.push_back(ee);
        }

        int EdgeComponent::NumElmtConnected() const
        {
            return int(m_elmtmap.size());
        }

        bool EdgeComponent::IsElmtConnected(int gvo_id, int locid) const
        {
            std::list<CompToElmt>::const_iterator def;
            CompToElmt ee(gvo_id,locid);

            def = find(m_elmtmap.begin(),m_elmtmap.end(),ee);

            // Found the element connectivity object in the list
            if(def != m_elmtmap.end())
            {
                return(true);
            }

            return(false);
        }

        /// \brief Get the orientation of edge1.
        ///
        /// If edge1 is connected to edge2 in the same direction as
        /// the points comprising edge1 then it is forward, otherwise
        /// it is backward.
        ///
        /// For example, assume edge1 is comprised of points 1 and 2,
        /// and edge2 is comprised of points 2 and 3, then edge is
        /// forward.
        ///
        /// If edge1 is comprised of points 2 and 1 and edge2 is
        /// comprised of points 3 and 2, then edge1 is backward.

        StdRegions::EdgeOrientation EdgeComponent::GetEdgeOrientation(const EdgeComponent &edge1,  const EdgeComponent &edge2)
        {
            StdRegions::EdgeOrientation returnval = StdRegions::eForwards;

            /// Backward direction.  Vertex 0 is connected to edge 2.
            if ((*edge1.GetVertex(0) == *edge2.GetVertex(0)) ||
                (*edge1.GetVertex(0) == *edge2.GetVertex(1)))
            {
                returnval = StdRegions::eBackwards;
            }
            // Not forward either, then we have a problem.
            else if ((*edge1.GetVertex(1) != *edge2.GetVertex(0)) &&
                (*edge1.GetVertex(1) != *edge2.GetVertex(1)))
            {
                std::ostrstream errstrm;
                errstrm << "Connected edges do not share a vertex. Edges ";
                errstrm << edge1.GetEid() << ", " << edge2.GetEid();
                ASSERTL0(false, errstrm.str());
            }

            return returnval;
        }
    }; //end of namespace
}; //end of namespace

/**
*    $Log: EdgeComponent.cpp,v $
*    Revision 1.10  2006/08/16 23:34:42  jfrazier
*    *** empty log message ***
*
*    Revision 1.9  2006/07/02 17:16:17  sherwin
*
*    Modifications to make MultiRegions work for a connected domain in 2D (Tris)
*
*    Revision 1.8  2006/06/02 18:48:40  sherwin
*    Modifications to make ProjectLoc2D run bit there are bus errors for order > 3
*
*    Revision 1.7  2006/05/30 14:00:04  sherwin
*    Updates to make MultiRegions and its Demos work
*
*    Revision 1.6  2006/05/23 20:19:58  jfrazier
*    Added #pragma to show where problems currently exist.
*
*    Revision 1.5  2006/05/23 19:56:33  jfrazier
*    These build and run, but the expansion pieces are commented out
*    because they would not run.
*
*    Revision 1.4  2006/05/16 20:12:59  jfrazier
*    Minor fixes to correct bugs.
*
*    Revision 1.3  2006/05/09 13:37:01  jfrazier
*    Removed duplicate definition of shared vertex pointer.
*
*    Revision 1.2  2006/05/06 20:36:16  sherwin
*    Modifications to get LocalRegions/Project1D working
*
*    Revision 1.1  2006/05/04 18:58:59  kirby
*    *** empty log message ***
*
*    Revision 1.14  2006/04/11 23:18:11  jfrazier
*    Completed MeshGraph2D for tri's and quads.  Not thoroughly tested.
*
*    Revision 1.13  2006/04/09 02:08:34  jfrazier
*    Added precompiled header.
*
*    Revision 1.12  2006/04/04 23:12:37  jfrazier
*    More updates to readers.  Still work to do on MeshGraph2D to store tris and quads.
*
*    Revision 1.11  2006/03/25 00:58:28  jfrazier
*    Many changes dealing with fundamental structure and reading/writing.
*
*    Revision 1.10  2006/03/12 14:20:42  sherwin
*
*    First compiling version of SpatialDomains and associated modifications
*
*    Revision 1.9  2006/03/12 11:06:39  sherwin
*
*    First complete copy of code standard code but still not compile tested
*
*    Revision 1.8  2006/03/12 07:42:02  sherwin
*
*    Updated member names and StdRegions call. Still has not been compiled
*
**/
<|endoftext|>