Monthly Archives: June 2010

Poco::SharedMemory

Poco::SharedMemory を紹介します。

このクラスは、名前のとおり Shared Memory(2つ以上のプロセスでのメモリ共有)を提供しています。

Poco::SharedMemory では2通りの実装があります:
ファイルベースの Shared Memory
(識別用の)名前ベースの Shared Memory

この両者を紹介しようと思ってサンプルを作っていたら、後者にバグを見つけたので、ここでは前者だけの紹介になります。
Bug Report は Forum に出しておきましたが、v1.3.7 に反映されるかは微妙かもしれません)
いずれバグが修正されたら、後者のサンプルも追加します。
(2010.07.06 追記:SharedMemory_POSIX.cpp には反映してもらえました
(2010.12.24 追記:v1.4.0 に反映されたのですが、さらに幾つか問題があって再提起しました。Bug Reportにその解決方法と、問題が解決した場合用に修正したコードの圧縮ファイルが置いてあります。)
(2011.02.14 追記:v1.4.1 で修正されたので更新しました)
(2011.02.15 追記:比較すべきバージョン番号を間違えていたので更新しました)

今回のサンプルは、Shared Memory の Server と Client、さらにそれらを起動するものと、3つのソースがあります。

SharedMemoryServer.cpp

・Shared Memory のサーバー。
Poco::Util::ServerApplication を使用。
・ServerTask を立ち上げ、その中で周期的に Shared Memory の内容を書き換え。
・最後の行の POCO_SERVER_MAIN は、その中に main() エントリーポイントがあるマクロ。
 (Util/include/Poco/Util/ServerApplication.h 参照)

#include <Poco/Util/ServerApplication.h>
#include <Poco/Task.h>
#include <Poco/TaskManager.h>
#include <Poco/SharedMemory.h>
#include <Poco/Format.h>
#include <Poco/Path.h>
#include <Poco/File.h>
#include <Poco/FileStream.h>
#ifndef POCO_VERSION
#include <Poco/Version.h>
#endif
 
#include <string>
#include <vector>
#include <iostream>
 
const std::string kSharedMemoryFileName("./MySharedMemory.txt");
const std::size_t kSharedMemorySize = 33;
const std::string kSharedMemoryName("MyTest");
 
class ServerTask: public Poco::Task
{
public:
	ServerTask() : Poco::Task("ServerTask")
	{
		Poco::Path path(kSharedMemoryFileName);
		m_File = Poco::File(path);
		if(!m_File.exists())
		{
			m_File.createFile();
			Poco::FileOutputStream ostr(m_File.path());
			for(std::size_t i=0; i<kSharedMemorySize; ++i)
			{
				ostr << ' ';
			}
		}
	}
 
	~ServerTask()
	{
		if(m_File.exists())
		{
			m_File.remove();
		}
	}
 
	void runTask()
	{
		Poco::Logger& logger = Poco::Util::Application::instance().logger();
 
#if (0x01040100 <= POCO_VERSION)
		Poco::SharedMemory sharedMemoryNamed(kSharedMemoryName, kSharedMemorySize, Poco::SharedMemory::AM_WRITE, 0, true);
#endif
		std::size_t loopCount = 0;
		while(!sleep(300))	// Poco::Task::sleep() should be called in favor of Thread::sleep()
		{
			Poco::SharedMemory sharedMemory(m_File, Poco::SharedMemory::AM_WRITE);
			for(std::size_t i=0; i<kSharedMemorySize; ++i)
			{
				sharedMemory.begin()[i] = static_cast<char>(('A' + loopCount + i) & 0x7F);
			}
			logger.information(Poco::format("ServerTask: %c - %c"
												, sharedMemory.begin()[0]
												, sharedMemory.end()[-1]));
#if (0x01040100 <= POCO_VERSION)
			for(std::size_t i=0; i<kSharedMemorySize; ++i)
			{
				sharedMemoryNamed.begin()[i] = static_cast<char>(('A' + loopCount + i) & 0x7F);
			}
			logger.information(Poco::format("ServerTask: %c - %c (named)"
												, sharedMemoryNamed.begin()[0]
												, sharedMemoryNamed.end()[-1]));
#endif
			++loopCount;
		}
	}
 
private:
	Poco::File	m_File;
};
 
class SharedMemoryServer: public Poco::Util::ServerApplication
{
public:
	SharedMemoryServer()
	{
	}
 
protected:
	void initialize(Application& self)
	{
		loadConfiguration(); // load default configuration files, if present
		Poco::Util::ServerApplication::initialize(self);
		logger().information("SharedMemoryServer starting up");
	}
 
	void uninitialize()
	{
		logger().information("SharedMemoryServer shutting down");
		Poco::Util::ServerApplication::uninitialize();
	}
 
	int main(const std::vector<std::string>& args)
	{
		Poco::TaskManager tm;
		tm.start(new ServerTask);
		waitForTerminationRequest();
		tm.cancelAll();
		tm.joinAll();
 
		return Application::EXIT_OK;
	}
};
 
POCO_SERVER_MAIN(SharedMemoryServer)

SharedMemoryClient.cpp

・Shared Memory のクライアント。
Poco::Util::ServerApplication を使用。
・ClientTask を立ち上げ、その中で周期的に Shared Memory の内容を読み出し。
・最後の行の POCO_SERVER_MAIN は、その中に main() エントリーポイントがあるマクロ。
 (Util/include/Poco/Util/ServerApplication.h 参照)

#include <Poco/Util/ServerApplication.h>
#include <Poco/Task.h>
#include <Poco/TaskManager.h>
#include <Poco/SharedMemory.h>
#include <Poco/Format.h>
#include <Poco/Path.h>
#include <Poco/File.h>
#ifndef POCO_VERSION
#include <Poco/Version.h>
#endif
 
#include <string>
#include <vector>
#include <iostream>
 
const std::string kSharedMemoryFileName("./MySharedMemory.txt");
const std::size_t kSharedMemorySize = 33;
const std::string kSharedMemoryName("MyTest");
 
class ClientTask: public Poco::Task
{
public:
	ClientTask() : Poco::Task("ClientTask")
	{
		Poco::Path path(kSharedMemoryFileName);
		m_File = Poco::File(path);
	}
 
	void runTask()
	{
		Poco::Logger& logger = Poco::Util::Application::instance().logger();
#if (0x01040100 <= POCO_VERSION)
		Poco::SharedMemory sharedMemoryNamed(kSharedMemoryName, kSharedMemorySize, Poco::SharedMemory::AM_READ, 0, false);
#endif
		while(!sleep(300))	// Poco::Task::sleep() should be called in favor of Thread::sleep()
		{
			Poco::SharedMemory sharedMemory(m_File, Poco::SharedMemory::AM_READ);
			logger.information(Poco::format("ClientTask: %c - %c"
						, sharedMemory.begin()[0]
						, sharedMemory.end()[-1]));
#if (0x01040100 <= POCO_VERSION)
			logger.information(Poco::format("ClientTask: %c - %c (named)"
						, sharedMemoryNamed.begin()[0]
						, sharedMemoryNamed.end()[-1]));
#endif
		}
	}
 
private:
	Poco::File	m_File;
};
 
class SharedMemoryClient: public Poco::Util::ServerApplication
{
public:
	SharedMemoryClient()
	{
	}
 
protected:
	void initialize(Application& self)
	{
		loadConfiguration(); // load default configuration files, if present
		Poco::Util::ServerApplication::initialize(self);
		logger().information("SharedMemoryClient starting up");
	}
 
	void uninitialize()
	{
		logger().information("SharedMemoryClient shutting down");
		Poco::Util::ServerApplication::uninitialize();
	}
 
	int main(const std::vector<std::string>& args)
	{
		Poco::TaskManager tm;
		tm.start(new ClientTask);
		waitForTerminationRequest();
		tm.cancelAll();
		tm.joinAll();
 
		return Application::EXIT_OK;
	}
};
 
POCO_SERVER_MAIN(SharedMemoryClient)

SharedMemoryTest.cpp

・Shared Memory のサーバーとクライアントプロセスを起動し、しばらく待った後にそれぞれを終了。

#include <Poco/Process.h>
#include <Poco/Pipe.h>
#include <Poco/PipeStream.h>
#include <Poco/StreamCopier.h>
#include <Poco/Format.h>
#include <Poco/Runnable.h>
#include <Poco/Thread.h>
 
#include <string>
#include <vector>
#include <iostream>
 
#include "ScopedLogMessage.h"
#include "PrepareConsoleLogger.h"
 
#if defined(POCO_OS_FAMILY_WINDOWS)
const std::string kCommandTable[] =	{"SharedMemoryTestServer.exe",
					 "SharedMemoryTestClient.exe"};
#else
const std::string kCommandTable[] =	{"./SharedMemoryTestServer",
					 "./SharedMemoryTestClient"};
#endif
 
const std::size_t kNumCommands = sizeof(kCommandTable)/sizeof(kCommandTable[0]);
 
class ProcessLauncher: public Poco::Runnable
{
public:
	ProcessLauncher(ScopedLogMessage& msg, const std::string& command) :
		m_msg(msg)
	,	m_command(command)
	,	m_pPh(NULL)
	,	m_Pid(0)
	{
	}
 
	~ProcessLauncher()
	{
		int rc = m_pPh->wait();
		m_msg.Message(Poco::format(" \"%s\" terminated with return code = %d", m_command, rc));
		delete m_pPh;
	}
 
	void run()
	{
		m_msg.Message(Poco::format(" launch \"%s\"", m_command));
		std::vector<std::string> args;
		Poco::Pipe outPipe;
		m_pPh = new Poco::ProcessHandle(Poco::Process::launch(m_command, args, 0, &outPipe, 0));
		m_Pid = m_pPh->id();
		Poco::PipeInputStream istr(outPipe);
		Poco::StreamCopier::copyStream(istr, std::cout);
	}
 
	void stop()
	{
		m_msg.Message(Poco::format(" \"%s\" requestTermination", m_command));
		Poco::Process::requestTermination(m_Pid);
	}
 
private:
	ScopedLogMessage&	m_msg;
	const std::string&	m_command;
	Poco::ProcessHandle*	m_pPh;
	Poco::Process::PID	m_Pid;
};
 
int main(int /*argc*/, char** /*argv*/)
{
	PrepareConsoleLogger logger(Poco::Logger::ROOT, Poco::Message::PRIO_INFORMATION);
 
	ScopedLogMessage msg("SharedMemoryTest ", "start", "end");
 
	Poco::Thread thread[kNumCommands];
	ProcessLauncher* pLauncher[kNumCommands];
 
	for(std::size_t i=0; i<kNumCommands; ++i)
	{
		pLauncher[i] = new ProcessLauncher(msg, kCommandTable[i]);
		thread[i].start(*pLauncher[i]);
		Poco::Thread::sleep(200);
	}
 
	Poco::Thread::sleep(2000);
 
	for(std::size_t i=0; i<kNumCommands; ++i)
	{
		std::size_t index = kNumCommands-1-i;
 
		pLauncher[index]->stop();
		thread[index].join();
		delete pLauncher[index];
	}
 
	return 0;
}

Results of execution

・On Linux and Macintosh OSX 10.6.4

[0] SharedMemoryTest start
[1]  launch "./SharedMemoryTestServer"
SharedMemoryServer starting up
[2]  launch "./SharedMemoryTestClient"
SharedMemoryClient starting up
ServerTask: A - a
ServerTask: A - a (named)
ClientTask: A - a
ClientTask: A - a (named)
ServerTask: B - b
ServerTask: B - b (named)
ClientTask: B - b
ClientTask: B - b (named)
ServerTask: C - c
ServerTask: C - c (named)
ClientTask: C - c
ClientTask: C - c (named)
ServerTask: D - d
ServerTask: D - d (named)
ClientTask: D - d
ClientTask: D - d (named)
ServerTask: E - e
ServerTask: E - e (named)
ClientTask: E - e
ClientTask: E - e (named)
ServerTask: F - f
ServerTask: F - f (named)
ClientTask: F - f
ClientTask: F - f (named)
ServerTask: G - g
ServerTask: G - g (named)
ClientTask: G - g
ClientTask: G - g (named)
[0]  "./SharedMemoryTestClient" requestTermination
SharedMemoryClient shutting down
[0]  "./SharedMemoryTestClient" terminated with return code = 0
[0]  "./SharedMemoryTestServer" requestTermination
SharedMemoryServer shutting down
[0]  "./SharedMemoryTestServer" terminated with return code = 0
[0] SharedMemoryTest end

・On Windows XP sp3

[0] SharedMemoryTest start
[1]  launch "SharedMemoryTestServer.exe"
SharedMemoryServer starting up
[2]  launch "SharedMemoryTestClient.exe"
SharedMemoryClient starting up
ServerTask: A - a
ServerTask: A - a (named)
ClientTask: A - a
ClientTask: A - a (named)
ServerTask: B - b
ServerTask: B - b (named)
ClientTask: B - b
ClientTask: B - b (named)
ServerTask: C - c
ServerTask: C - c (named)
ClientTask: C - c
ClientTask: C - c (named)
ServerTask: D - d
ServerTask: D - d (named)
ClientTask: D - d
ClientTask: D - d (named)
ServerTask: E - e
ServerTask: E - e (named)
ClientTask: E - e
ClientTask: E - e (named)
ServerTask: F - f
ServerTask: F - f (named)
ClientTask: F - f
ClientTask: F - f (named)
ServerTask: G - g
ServerTask: G - g (named)
ClientTask: G - g
ClientTask: G - g (named)
[0]  "SharedMemoryTestClient.exe" requestTermination
SharedMemoryClient shutting down
[0]  "SharedMemoryTestClient.exe" terminated with return code = 0
[0]  "SharedMemoryTestServer.exe" requestTermination
SharedMemoryServer shutting down
[0]  "SharedMemoryTestServer.exe" terminated with return code = 0
[0] SharedMemoryTest end

Downloads

ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2013.05.31 updated)
・2010年6月29日からのダウンロード数:919

Subversion

・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。

Reference

http://pocoproject.org にある Processes のプレセンテーション。(PDF)

Powered by POCO Copyright © 2010 Round Square Inc. All rights reserved.